std::jthread::jthread
出自cppreference.com
| |
(1) | (C++20 起) |
| |
(2) | (C++20 起) |
| |
(3) | (C++20 起) |
| |
(4) | (C++20 起) |
構造新的 std::jthread 對象。
1) 構造不表示線程的新
std::jthread 對象。2) 移動構造函數。構造的
std::jthread 對象表示之前由 other 表示的執行線程。此調用後 other 不再表示執行線程。3) 創建與執行線程關聯的新
std::jthread 對象。新的執行線程開始執行:
std::invoke(decay-copy(std::forward<F>(f)), get_stop_token(), decay-copy(std::forward<Args>(args))...)
|
(C++23 前) |
|
|
(C++23 起) |
如果以上表達式非良構,那麼就會開始執行:
std::invoke(decay-copy(std::forward<F>(f)), decay-copy(std::forward<Args>(args))...).
|
(C++23 前) |
|
|
(C++23 起) |
對 decay-copy 的調用會在當前線程求值(C++23 前)
auto 產生的值會在當前線程實質化(C++23 起),所以實參的求值和複製/移動中拋出的任何異常都會在當前線程拋出,而不會開始新線程。 這些重載只有在
std::remove_cvref_t<F> 和 std::jthread 不是同一類型時才會參與重載決議。 如果以下任意值是
false,那麼程序非良構:
std::is_constructible_v<std::decay_t<F>, F>(std::is_constructible_v<std::decay_t<Args>, Args> && ...)std::is_invocable_v<std::decay_t<F>, std::decay_t<Args>...> ||std::is_invocable_v<std::decay_t<F>, std::stop_token, std::decay_t<Args>...>
構造函數的調用完成同步於新的執行線程上
f 副本的調用開始。4) 複製構造函數被棄置;線程不可複製。兩個
std::jthread 對象不能表示同一執行線程。參數
| other | - | 用來構造此 std::jthread 對象的另一 std::jthread 對象
|
| f | - | 在新線程執行的可調用 (Callable) 對象 |
| args | - | 傳遞給函數的參數 |
後條件
1)
get_id() 等於 std::jthread::id()(即 joinable() 返回 false)而 get_stop_source().stop_possible() 是 false。3)
get_id() 不等於 std::jthread::id()(即 joinable() 返回 true),且 get_stop_source().stop_possible() 是 true。異常
3) 如果不能開始線程,那麼就會拋出 std::system_error。異常可能表示錯誤條件
std::errc::resource_unavailable_try_again 或另一實現限定的錯誤條件。註解
線程函數的實參是移動或按值複製的。如果需要傳遞引用實參給線程函數,那麼必須包裝它(例如用 std::ref 或 std::cref)。
忽略來自函數的任何返回值。如果函數拋出異常,那麼就會調用 std::terminate。需要將返回值或異常傳遞迴調用方線程時可以使用 std::promise 或 std::async。
示例
運行此代碼
#include <chrono>
#include <iostream>
#include <thread>
#include <utility>
using namespace std::literals;
void f1(int n)
{
for (int i = 0; i < 5; ++i)
{
std::cout << "正在执行线程 1\n";
++n;
std::this_thread::sleep_for(10ms);
}
}
void f2(int& n)
{
for (int i = 0; i < 5; ++i)
{
std::cout << "正在执行线程 2\n";
++n;
std::this_thread::sleep_for(10ms);
}
}
class foo
{
public:
void bar()
{
for (int i = 0; i < 5; ++i)
{
std::cout << "正在执行线程 3\n";
++n;
std::this_thread::sleep_for(10ms);
}
}
int n = 0;
};
class baz
{
public:
void operator()()
{
for (int i = 0; i < 5; ++i)
{
std::cout << "正在执行线程 4\n";
++n;
std::this_thread::sleep_for(10ms);
}
}
int n = 0;
};
int main()
{
int n = 0;
foo f;
baz b;
std::jthread t0; // t0 不是线程
std::jthread t1(f1, n + 1); // 按值传递
std::jthread t2a(f2, std::ref(n)); // 按引用传递
std::jthread t2b(std::move(t2a)); // t2b 现在运行 f2()。t2a 不再是线程
std::jthread t3(&foo::bar, &f); // t3 在对象 f 上运行 foo::bar()
std::jthread t4(b); // t4 在对象 b 的副本上运行 baz::operator()
t1.join();
t2b.join();
t3.join();
std::cout << "n 的最终值是 " << n << '\n';
std::cout << "f.n (foo::n) 的最终值是 " << f.n << '\n';
std::cout << "b.n (baz::n) 的最终值是 " << b.n << '\n';
// t4 在析构时合并
}
可能的輸出:
正在执行线程 2
正在执行线程 1
正在执行线程 4
正在执行线程 3
正在执行线程 3
正在执行线程 4
正在执行线程 2
正在执行线程 1
正在执行线程 3
正在执行线程 1
正在执行线程 4
正在执行线程 2
正在执行线程 3
正在执行线程 1
正在执行线程 4
正在执行线程 2
正在执行线程 3
正在执行线程 1
正在执行线程 4
正在执行线程 2
n 的最终值是 5
f.n (foo::n) 的最终值是 5
b.n (baz::n) 的最终值是 0
缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
| 缺陷報告 | 應用於 | 出版時的行為 | 正確行為 |
|---|---|---|---|
| LWG 3476 | C++20 | 重載 (3) 直接要求 F 和實參類型(在退化後)必須可移動構造
|
移除這些要求[1] |
- ↑ std::is_constructible_v 已間接要求了可移動構造性。
參閱
構造新的 thread 對象 ( std::thread 的公開成員函數)
| |
thrd_create 的 C 文檔
| |