jthread() noexcept; // (1) C++20
template <class F, class... Args>
explicit jthread(F&& f, Args&&... args); // (2) C++20
template <class... Args>
explicit jthread(Args&&... args); // (2) C++29
jthread(const jthread&) = delete; // (3) C++20
jthread(jthread&&) noexcept; // (4) C++20
概要
- (1) : デフォルトコンストラクタ。新しいスレッドを生成せず、空の状態にする。
-
(2) : 新しいスレッドを生成し、そのスレッド上で関数オブジェクトを呼び出す
- C++20 : 第1引数
fが呼び出す関数オブジェクトであり、残りの引数args...がfへ渡す実引数となる -
C++29 : シグニチャがすべての引数を1つのパック
args...で受け取る形へ変更され、関数オブジェクトより前に0個以上のスレッド属性(name_hint・stack_size_hintなど)も渡せるようになった。args...の先頭に連続するスレッド属性型の引数がスレッドの名前やスタックサイズなどの設定として使われ、最初に現れた非属性型の引数が呼び出す関数オブジェクト、それ以降がその関数オブジェクトへ渡す実引数として扱われる。同じ属性型を複数回渡した場合、プログラムは不適格となるstd::jthread t{std::jthread::name_hint("Worker"), f, 42};
- C++20 : 第1引数
-
(3) : コピーコンストラクタ。コピー不可。
- (4) : ムーブコンストラクタ。スレッドの所有権を移動する。
テンプレートパラメータ制約
- (2) :
remove_cvref_t<F>がjthreadではないことdecay_t<F>およびdecay_t<Args>の各型がCpp17MoveConstructible要件を満たすこと
適格要件
- (2) : 以下の条件がすべて
trueであることis_constructible_v<decay_t<F>, F>(is_constructible_v<decay_t<Args>, Args> && ...)is_move_constructible_v<decay_t<F>>(is_move_constructible_v<decay_t<Args>> && ...)is_invocable_v<decay_t<F>, decay_t<Args>...> || is_invocable_v<decay_t<F>, stop_token, decay_t<Args>...>
効果
-
(2) :
- メンバ変数として保持している
std::stop_source型オブジェクトを初期化する -
以下の式が有効であればそれで新たなスレッドを生成して実行し、
invoke(auto(std::forward<F>(f)), get_stop_token(), auto(std::forward<Args>(args))...) -
そうでなければ以下の式でスレッドを生成して実行する
invoke(auto(std::forward<F>(f)), auto(std::forward<Args>(args))...) -
この呼び出しでの戻り値は無視される。この関数呼び出しが例外を送出する場合、呼び出し元スレッドで
std::terminateが呼び出される
- メンバ変数として保持している
同期操作
- (2) : 同コンストラクタの呼び出し完了は、fのコピーの呼び出し開始に対して同期する。つまり、「コンストラクタ呼び出し側スレッドT0でのコンストラクタ呼び出し完了」は、「新しいスレッド
T1上でのfのコピーの呼び出し開始」よりも前に発生する。
事後条件
- (1) :
get_id() == id()がtrueとなることget_stop_source()で取得されるstd::stop_sourceオブジェクトのstop_possible()がfalseであること
- (2) :
get_id() != id()がtrueとなることget_stop_source()で取得されるstd::stop_sourceオブジェクトのstop_possible()がtrueであること*thisは新しいスレッドと関連付けられること
- (4) :
x.get_id() == get_id()がtrueであることget_id()がムーブ前のx.get_id()の値であることx.get_stop_source().stop_possible()がfalseであること
例外
-
(2) : 新しいスレッドの作成に失敗した場合、
system_error例外を投げる。その例外オブジェクトには、以下のエラー状態が設定されうる:resource_unavailable_try_again: 新たなスレッドを作るためのリソースが不足している。もしくはシステムやプロセスが規定するスレッド数の上限を超過した。
例
基本的な使い方
#include <iostream>
#include <cstdint>
#include <thread>
#include <chrono>
std::uint64_t sum1 = 0;
std::uint64_t sum2 = 0;
void f1(std::stop_token stoken, std::uint64_t n)
{
sum1 = 0;
for (std::uint64_t i = 1; i < n; ++i) {
if (stoken.stop_requested()) {
// 中断リクエストがきたのでスレッドを終了する
break;
}
sum1 += i;
}
}
void f2(std::uint64_t n)
{
sum2 = 0;
for (std::uint64_t i = 1; i < n; ++i) {
sum2 += i;
}
}
int main()
{
{
// 関数の第1引数がstd::stop_token型である場合、
// スレッドに中断リクエストを送れるようになる
std::jthread jt1 {f1, 1'000'000};
std::this_thread::sleep_for(std::chrono::milliseconds{3});
jt1.request_stop(); // スレッドの中断要求を発行
// スレッド実行する関数がstd::stop_tokenを受け取らない場合、
// 中断リクエストを使用せず、
// デストラクタで自動的にjoinするスレッドオブジェクトとして使用する
std::jthread jt2 {
[] { f2(1'000'000); }
};
} // jthreadのデストラクタでは、中断要求を発行し、スレッドの終了を待機する
std::cout << sum1 << std::endl; // 計算できたところまで表示
std::cout << sum2 << std::endl;
}
出力例
48458670270
499999500000
スレッド属性を指定する (C++29)
#include <thread>
#include <iostream>
#include <pthread.h> // POSIX環境
void work(int n)
{
// ...
}
int main()
{
// スレッド名とスタックサイズのヒントを指定してスレッドを生成する。
// スレッド名はデバッガのスレッド一覧などに表示される
std::jthread t{
std::jthread::name_hint("Worker"),
std::jthread::stack_size_hint(512 * 1024),
work,
42
};
// 標準ライブラリにスレッド名を取得するAPIはないが、
// ネイティブハンドルを通じてプラットフォームのAPIで取得できる
char name[16]{};
pthread_getname_np(t.native_handle(), name, sizeof(name));
std::cout << name << std::endl;
}
出力例
Worker
バージョン
言語
- C++20
処理系
- Clang:
- GCC: 10.2.0 ✅
- Visual C++: ??
関連項目
参照
- LWG Issue 3476.
threadandjthreadconstructors require that the parameters be move-constructible- C++23で、
is_constructible要件が既に目的を満たすため、冗長だったムーブ構築可能(is_move_constructible)の要件が削除された
- C++23で、
- P0849R8
auto(x): decay-copy in the language- C++23で、実引数のコピーを表す規定が、説明専用の
decay-copyから言語機能のauto(x)へ置き換えられた
- C++23で、実引数のコピーを表す規定が、説明専用の
- P2019R9 Thread attributes
- C++29で、スレッド属性を先頭引数として渡せるようになった