std::thread::thread
来自cppreference.com
thread() noexcept; | (1) | (C++11 起) |
thread( thread&& other ) noexcept; | (2) | (C++11 起) |
template< class F, class... Args > explicit thread( F&& f, Args&&... args ); | (3) | (C++11 起) |
thread( const thread& ) = delete; | (4) | (C++11 起) |
构造新的 std::thread
对象。
1) 构造不表示线程的新
std::thread
对象。2) 移动构造函数。构造表示 other 曾表示的执行线程的
std::thread
对象。此调用后 other 不再表示执行线程。3) 创建新的
std::thread
对象并将它与执行线程关联。新的执行线程开始执行: INVOKE(decay-copy(std::forward<F>(f)), | (C++23 前) |
std::invoke(auto(std::forward<F>(f)), | (C++23 起) |
对 decay-copy 的调用会在当前线程求值(C++23 前)auto 产生的值会在当前线程实质化(C++23 起),所以任何求值和复制/移动参数中抛出的异常都会抛出到当前线程,而不会开始新线程。
此重载只有在 std::decay<F>::type(C++20 前)std::remove_cvref_t<F>(C++20 起) 和
std::thread
不是同一类型时才会参与重载决议。 如果满足以下任意条件,那么程序非良构:
| (C++20 前) |
如果以下任意值是 false,那么程序非良构:
| (C++20 起) |
构造函数的调用完成同步于新的执行线程上 f 副本的调用开始。
4) 复制构造函数被弃置;
std::thread
不可复制。两个 std::thread
对象不能表示同一执行线程。参数
other | - | 用来构造此线程对象的另一线程对象 |
f | - | 在新线程执行的可调用 (Callable) 对象 |
args | - | 传递给新函数的实参 |
后条件
异常
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> void f1(int n) { for (int i = 0; i < 5; ++i) { std::cout << "正在执行线程1\n"; ++n; std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } void f2(int& n) { for (int i = 0; i < 5; ++i) { std::cout << "正在执行线程2\n"; ++n; std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } class foo { public: void bar() { for (int i = 0; i < 5; ++i) { std::cout << "正在执行线程3\n"; ++n; std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } 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(std::chrono::milliseconds(10)); } } int n = 0; }; int main() { int n = 0; foo f; baz b; std::thread t1; // t1 不是线程 std::thread t2(f1, n + 1); // 按值传递 std::thread t3(f2, std::ref(n)); // 按引用传递 std::thread t4(std::move(t3)); // t4 现在运行 f2()。t3 不再是线程 std::thread t5(&foo::bar, &f); // t5 在对象 f 上运行 foo::bar() std::thread t6(b); // t6 在对象 b 的副本上运行 baz::operator() t2.join(); t4.join(); t5.join(); t6.join(); std::cout << "n 的最终值是 " << n << '\n'; std::cout << "f.n (foo::n) 的最终值是 " << f.n << '\n'; std::cout << "b.n (baz::n) 的最终值是 " << b.n << '\n'; }
可能的输出:
正在执行线程1 正在执行线程2 正在执行线程3 正在执行线程4 正在执行线程3 正在执行线程1 正在执行线程2 正在执行线程4 正在执行线程2 正在执行线程3 正在执行线程1 正在执行线程4 正在执行线程3 正在执行线程2 正在执行线程1 正在执行线程4 正在执行线程3 正在执行线程1 正在执行线程2 正在执行线程4 n 的最终值是 5 f.n (foo::n) 的最终值是 5 b.n (baz::n) 的最终值是 0
缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 出版时的行为 | 正确行为 |
---|---|---|---|
LWG 2097 | C++11 | 对于重载 (3),F 可以是 std::thread | 对 F 进行约束 |
LWG 3476 | C++20 | 重载 (3) 直接要求 F 和实参类型(在退化后)必须可移动构造 | 移除这些要求[1] |
- ↑ std::is_constructible_v 已间接要求了可移动构造性。
引用
- C++23 标准(ISO/IEC 14882:2024):
- 33.4.3.3 thread constructors [thread.thread.constr]
- C++20 标准(ISO/IEC 14882:2020):
- 32.4.2.2 thread constructors [thread.thread.constr]
- C++17 标准(ISO/IEC 14882:2017):
- 33.3.2.2 thread constructors [thread.thread.constr]
- C++14 标准(ISO/IEC 14882:2014):
- 30.3.1.2 thread constructors [thread.thread.constr]
- C++11 标准(ISO/IEC 14882:2011):
- 30.3.1.2 thread constructors [thread.thread.constr]
参阅
创建新的 jthread 对象 ( std::jthread 的公开成员函数) | |
thrd_create 的 C 文档 |