std::jthread::jthread
来自cppreference.com
<tbody>
</tbody>
jthread() noexcept; |
(1) | (C++20 起) |
jthread( jthread&& other ) noexcept; |
(2) | (C++20 起) |
template< class F, class... Args > explicit jthread( F&& f, Args&&... args ); |
(3) | (C++20 起) |
jthread( const jthread& ) = delete; |
(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 文档
| |