C++20 多线程 std::jthread
在C++20中新加了jthread类,jthread是对thread的一种封装
std::jthread 构造函数
(1)jthread() noexcept;
(2)jthread( jthread&& other ) noexcept;
(3)template< class Function, class... Args >
explicit jthread( Function&& f, Args&&... args );
(4)jthread( const jthread& ) = delete;
(1) 构造新的 jthread 对象,但由于没有传入函数,所以 jthread 对象还没有关联到线程。
(2) 移动构造函数。构造的 jthread 对象表示之前由 other 表示的执行线程。此调用后 other 不再表示执行线程。
(3) 创建与执行线程关联的新 std::jthread 对象。若函数 f 接受 std::stop_token 作为其首参数,则新线程开始执行。
(4) 复制构造函数被删除;线程不可复制。
#include <iostream>
#include <utility>
#include <thread>
#include <chrono>
void f1(int n)
{
for (int i = 0; i < 5; ++i) {
std::cout << "Thread 1 executing\n";
++n;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void f2(int& n)
{
for (int i = 0; i < 5; ++i) {
std::cout << "Thread 2 executing\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 << "Thread 3 executing\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 << "Thread 4 executing\n";
++n;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
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 << "Final value of n is " << n << '\n';
std::cout << "Final value of foo::n is " << f.n << '\n';
// t4 在析构时结合
}
观察器
joinable
[[nodiscard]] bool joinable() const noexcept;
#include <iostream>
#include <thread>
#include <chrono>
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::jthread t;
std::cout << "before starting, joinable: " << std::boolalpha << t.joinable()
<< '\n';
t = std::thread(foo);
std::cout << "after starting, joinable: " << t.joinable()
<< '\n';
t.join();
std::cout << "after joining, joinable: " << t.joinable()
<< '\n';
}
输出信息:
before starting, joinable: false
after starting, joinable: true
after joining, joinable: false
get_id
[[nodiscard]] std::jthread::id get_id() const noexcept;
返回标识与 *this 关联的线程的 std::jthread::id 。
#include <iostream>
#include <thread>
#include <chrono>
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::jthread t1(foo);
std::jthread::id t1_id = t1.get_id();
std::jthread t2(foo);
std::jthread::id t2_id = t2.get_id();
std::cout << "t1's id: " << t1_id << '\n';
std::cout << "t2's id: " << t2_id << '\n';
}
输出信息:
t1's id: 0x35a7210f
t2's id: 0x35a311c4
操作
join
void join();
阻塞当前线程直至 *this 所标识的线程结束其执行。*this 所标识的线程的完成同步于对应的从 join() 成功返回。*this 自身上不进行同步。同时从多个线程在同一 jthread 对象上调用 join() 构成数据竞争,导致未定义行为。
#include <iostream>
#include <thread>
#include <chrono>
void foo()
{
// 模拟昂贵操作
std::this_thread::sleep_for(std::chrono::seconds(1));
}
void bar()
{
// 模拟昂贵操作
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::cout << "starting first helper...\n";
std::jthread helper1(foo);
std::cout << "starting second helper...\n";
std::jthread helper2(bar);
std::cout << "waiting for helpers to finish..." << std::endl;
helper1.join();
helper2.join();
std::cout << "done!\n";
}
输出信息:
starting first helper...
starting second helper...
waiting for helpers to finish...
done!
detach
void detach();
从 jthread 对象分离执行线程,允许线程独立地运行。一旦该线程退出,则释放任何分配的资源。调用 detach 后 *this 不再占有任何线程。
#include <iostream>
#include <chrono>
#include <thread>
void independentThread()
{
std::cout << "Starting concurrent thread.\n";
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Exiting concurrent thread.\n";
}
void threadCaller()
{
std::cout << "Starting thread caller.\n";
std::jthread t(independentThread);
t.detach();
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Exiting thread caller.\n";
}
int main()
{
threadCaller();
std::this_thread::sleep_for(std::chrono::seconds(5));
}
输出信息:
Starting thread caller.
Starting concurrent thread.
Exiting thread caller.
Exiting concurrent thread.
swap
void swap( std::jthread& other ) noexcept;
交换二个 jthread 对象的底层柄。
#include <iostream>
#include <thread>
#include <chrono>
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
void bar()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::jthread t1(foo);
std::jthread t2(bar);
std::cout << "thread 1 id: " << t1.get_id() << '\n'
<< "thread 2 id: " << t2.get_id() << '\n';
std::swap(t1, t2);
std::cout << "after std::swap(t1, t2):" << '\n'
<< "thread 1 id: " << t1.get_id() << '\n'
<< "thread 2 id: " << t2.get_id() << '\n';
t1.swap(t2);
std::cout << "after t1.swap(t2):" << '\n'
<< "thread 1 id: " << t1.get_id() << '\n'
<< "thread 2 id: " << t2.get_id() << '\n';
}
输出信息:
thread 1 id: 140185268262656
thread 2 id: 140185259869952
after std::swap(t1, t2):
thread 1 id: 140185259869952
thread 2 id: 140185268262656
after t1.swap(t2):
thread 1 id: 140185268262656
thread 2 id: 140185259869952
停止记号处理
get_stop_source
std::stop_source get_stop_source() const noexcept;
返回 std::stop_source ,拥有与 jthread 对象内部所保有者相同的共享停止状态。
get_stop_token
std::stop_token get_stop_token() const noexcept;
返回 std::stop_token ,与 jthread 对象内部保有的同一共享停止状态关联。
request_stop
bool request_stop() noexcept;
若内部停止状态尚未被请求停止,则对它发出停止请求。原子地作出确定,而若请求了停止,则原子地更新共享状态以避免竞争条件,使得:能在同一共享状态的 std::stop_token 与 std::stop_source 上同时调用 stop_requested() 与 stop_possible() 能从多个线程在同一 jthread 对象或与同一停止状态关联的其他 std::stop_source 对象上并发调用 request_stop() ,而将只有一个线程实际进行停止请求。
注意:若 request_stop() 发出停止请求(即返回 true ),则将在发出 request_stop() 的同一线程上同步调用对同一共享停止状态注册的任何 std::stop_callbacks 。若任何回调的调用经由异常退出,则调用 std::terminate 。
若已作出停止请求,则此函数返回 false 。然而不保证正好对同一停止状态(成功)请求停止的另一线程或 std::stop_source 对象不仍然在调用 std::stop_callback 函数的中间。
若 request_stop() 发出停止请求(即返回 true ),则提醒所有用与 jthread 的内部停止状态关联的 stop_token 的可中断等待注册的、基类型为 std::condition_variable_any 的条件变量。
管理当前线程的函数
yield()
void yield() noexcept;
提供提示给实现,以重调度线程的执行,允许其他线程运行。
#include <iostream>
#include <chrono>
#include <thread>
// 建议其他线程运行一小段时间的“忙睡眠”
void little_sleep(std::chrono::microseconds us)
{
auto start = std::chrono::high_resolution_clock::now();
auto end = start + us;
do {
std::this_thread::yield();
} while (std::chrono::high_resolution_clock::now() < end);
}
int main()
{
auto start = std::chrono::high_resolution_clock::now();
little_sleep(std::chrono::microseconds(100));
auto elapsed = std::chrono::high_resolution_clock::now() - start;
std::cout << "waited for "
<< std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count()
<< " microseconds\n";
}
输出信息:
waited for 128 microseconds
get_id()
std::thread::id get_id() noexcept;
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
std::mutex g_display_mutex;
void foo()
{
std::thread::id this_id = std::this_thread::get_id();
g_display_mutex.lock();
std::cout << "thread " << this_id << " sleeping...\n";
g_display_mutex.unlock();
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::thread t1(foo);
std::thread t2(foo);
t1.join();
t2.join();
}
输出信息:
thread 0x2384b312 sleeping...
thread 0x228a10fc sleeping...
sleep_for()
template< class Rep, class Period >
void sleep_for( const std::chrono::duration<Rep, Period>& sleep_duration );
阻塞当前线程执行,至少经过指定的 sleep_duration 。此函数可能阻塞长于 sleep_duration ,因为调度或资源争议延迟。标准库建议用稳定时钟度量时长。若实现用系统时间代替,则等待时间亦可能对时钟调节敏感。
#include <iostream>
#include <chrono>
#include <thread>
int main()
{
using namespace std::chrono_literals; // C++14
std::cout << "Hello waiter" << std::endl; // 有意冲入
auto start = std::chrono::high_resolution_clock::now();
std::this_thread::sleep_for(2s);
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> elapsed = end-start;
std::cout << "Waited " << elapsed.count() << " ms\n";
}
输出信息:
Hello waiter
Waited 2000.12 ms
sleep_until()
template< class Clock, class Duration >
void sleep_until( const std::chrono::time_point<Clock,Duration>& sleep_time );
阻塞当前线程,直至抵达指定的 sleep_time 。使用联倾向于 sleep_time 的时钟,这表示时钟调节有影响。从而在调用时间点后,阻塞的时长可能小于,但不会多于 sleep_time - Clock::now() 。函数亦可能阻塞长于抵达 sleep_time 之后,由于调度或资源争议延迟。
C++20 多线程 std::jthread的更多相关文章
- c/c++ 多线程 std::call_once的应用
多线程 std::call_once的应用 std::call_once的应用:类成员的延迟初始化,并只初始化一次.和static的作用很像,都要求是线程安全的,c++11之前在多线程的环境下,sta ...
- c/c++ 多线程 std::call_once
多线程 std::call_once 转自:https://blog.csdn.net/hengyunabc/article/details/33031465 std::call_once的特点:即使 ...
- c/c++ 多线程 std::lock
多线程 std::lock 当要同时操作2个对象时,就需要同时锁定这2个对象,而不是先锁定一个,然后再锁定另一个.同时锁定多个对象的方法:std::lock(对象1.锁,对象2.锁...) 额外说明: ...
- C++11多线程std::thread的简单使用
在cocos2dx 2.0时代,我们使用的是pthread库,是一套用户级线程库,被广泛地使用在跨平台应用上.但在cocos2dx 3.0中并未发现有pthread的支持文件,原来c++11中已经拥有 ...
- 多线程std::cout 深入研究
1.研究背景 在测试时发现mingw版本的gcc编译出来的程序,一个主程序新建20个线程,每个线程都循环向cout输出信息,几分钟程序就崩了,而用msvc和gcc-linaro版gcc交叉编译器编译出 ...
- Cocos2dx 3.0 过渡篇(二十六)C++11多线程std::thread的简单使用(上)
昨天练车时有一MM与我交替着练,聊了几句话就多了起来,我对她说:"看到前面那俩教练没?老色鬼两枚!整天调戏女学员."她说:"还好啦,这毕竟是他们的乐趣所在,你不认为教练每 ...
- C++11并发——多线程std::thread (一)
https://www.cnblogs.com/haippy/p/3284540.html 与 C++11 多线程相关的头文件 C++11 新标准中引入了四个头文件来支持多线程编程,他们分别是< ...
- Cocos2dx 3.0 过渡篇(二十七)C++11多线程std::thread的简单使用(下)
本篇接上篇继续讲:上篇传送门:http://blog.csdn.net/star530/article/details/24186783 简单的东西我都说的几乎相同了,想挖点深的差点把自己给填进去. ...
- C++11并发编程:多线程std::thread
一:概述 C++11引入了thread类,大大降低了多线程使用的复杂度,原先使用多线程只能用系统的API,无法解决跨平台问题,一套代码平台移植,对应多线程代码也必须要修改.现在在C++11中只需使用语 ...
随机推荐
- 解决vue侧边栏一级菜单问题
最近我在学习vue,然后遇到一个问题,就是跟着视频里面的代码敲,出现了一些不好解决的问题 这是两个一级目录,我遇到的问题就是点击第一个一级目录,另外一个一级目录也会展开, 前端代码是这样的,和视频里面 ...
- 理解 JAVABEAN EJB POJO
<Spring实战>第一章:基本理念.各类名称熟语 POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans,是为了避免和EJB混 ...
- Apache shiro权限基本使用
l shiro框架的核心功能:认证.授权.会话管理.加密 Application Code:应用程序代码,由开发人员负责开发的 Subject:框架提供的接口,代表当前用户对象 SecurityMan ...
- python_购物车
流程图 实现方式 #!/usr/bin/python3 __author__ = 'beimenchuixue' __blog__ = 'http://www.cnblogs.com/2bjiuji ...
- newifi3-D2 openwrt挂载u盘扩容/overlay
格式化U盘 1.openwrt安装插件 opkg install fdisk swap-utils kmod-usb-storage kmod-fs-ext4 e2fsprogs kmod-usb-o ...
- PCA基本原理
降维问题的优化目标:将一组N维向量降维k维(K大于0,小于N),其目标是选择K个单位(模为1)正交基,使得原始数据变换到这组基上后, 选择然数据点之间方差最大的方向作为坐标轴 各字段两两间协方差为0, ...
- Python正则表达式 re.sub()函数:标志位flags与参数个数问题
这两天在写爬虫程序,涉及英文文本处理,需要规范化英文标点符号的写法.正常情况下,英文句号「.」后面需要保证有且只有一个空格,但也有例外情况,比如「i.e.」.「e.g.」.「P.S.」这种.由于无法预 ...
- helm部署的服务如何修改配置
关于helm部署服务 在Kubernetes上进行容器化部署时,使用helm可以简化操作,以部署Jenkins为例,只需要以下命令即可完成部署: helm install --namespace he ...
- Win7 64X 安装VisualSVNServer 2.6.0过程中出现Custom action InstallWMISchemaExcute failed: Cannot query proxy blanket: No such interface supported (0x80004002)
Win7 64X 安装VisualSVNServer 2.6.0过程中出现错误:Custom action InstallWMISchemaExcute failed: Cannot query pr ...
- Code Test(2)
1110test T1:Inversion逆序数对1(inversion.cpp/in/out 1s 256M)给定N的值,要求找出一个N的全排列,这个全排列中,逆序数有M对.这样的结果会存在多个解, ...