jthread() noexcept; // (1) C++20
template <class F, class... Args>
explicit jthread(F&& f, Args&&... args); // (2) C++20
jthread(const jthread&) = delete; // (3) C++20
jthread(jthread&&) noexcept; // (4) C++20
概要
- (1) : デフォルトコンストラクタ。新しいスレッドを生成せず、空の状態にする。
- (2) : 新しいスレッドを生成し、そのスレッド上で引数
args...
を渡して、関数オブジェクトf
を呼び出す。 - (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(decay-copy(std::forward<F>(f)), get_stop_token(), decay-copy(std::forward<Args>(args))...)
-
そうでなければ以下の式でスレッドを生成して実行する
invoke(decay-copy(std::forward<F>(f)), decay-copy(std::forward<Args>(args))...)
-
この呼び出しでの戻り値は無視される。この関数呼び出しが例外を送出する場合、呼び出し元スレッドで
std::terminate
が呼び出される - (3) :
- メンバ変数として保持している
同期
- (2) : コンストラクタ呼び出しの完了は、
f
のコピーの呼び出し開始と同期する
新しいスレッドを生成し、INVOKE(DECAY_COPY(std::forward<F>(f)), DECAY_COPY(std::forward<Args>(args))...)
を実行する。ただしDECAY_COPY
は同コンストラクタを呼び出したスレッド上にて評価される。またf
のコピーの戻り値は無視される。
- DECAY_COPY(x)
は template <class T> typename std::decay<T>::type decay_copy(T&& v) { return std::forward<T>(v); }
と定義される。おおよそ、x
が配列型なら先頭要素へのポインタ、x
が関数型ならその関数ポインタ、x
がコピーコンストラクト可能な型ならx
からコピーされたオブジェクト、x
がムーブコンストラクト可能な型ならx
からムーブされたオブジェクトとなる。
同期
- (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>
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 t1 {f1, 1'000'000};
std::this_thread::sleep_for(std::chrono::milliseconds{3});
t1.request_stop(); // スレッドの中断要求を発行
// スレッド実行する関数がstd::stop_tokenを受け取らない場合、
// 中断リクエストを使用せず、
// デストラクタで自動的にjoinするスレッドオブジェクトとして使用する
std::jthread t2 {
[] { f2(1'000'000); }
};
} // jthreadのデストラクタでは、中断要求を発行し、スレッドの終了を待機する
std::cout << sum1 << std::endl; // 計算できたところまで表示
std::cout << sum2 << std::endl;
}
出力例
48458670270
499999500000
バージョン
言語
- C++20
処理系
- Clang:
- GCC: 10.2.0 ✅
- Visual C++: ??