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中只需使用语 ...
随机推荐
- JVM运行时数据区划分
Java内存空间 内存是非常重要的系统资源,是硬盘和cpu的中间仓库及桥梁,承载着操作系统和应用程序的实时运行.JVM内存布局规定了JAVA在运行过程中内存申请.分配.管理的策略,保证了JVM的高效稳 ...
- Typora基础使用
Markdown学习 标题 三级标题 四级标题 字体 Hello,World! Hello,World! Hello,World! Hello,World! 引用 选择狂神说Java,走向人生巅峰 分 ...
- java四种修饰符的限制范围
转自https://blog.csdn.net/lch_2016/article/details/81052343 访问权限 本类 本包 不同包子类 不同包非子类 public √ √ √ √ pro ...
- Java中的lambda匿名函数使用
Java中的lambda匿名函数使用 lambda匿名函数的使用是为了满足某些情况下需要临时定义函数,或者事先定义,需要时才使用.在python里面,lambda表达式的表达方式为:lambda 参数 ...
- redis命令和RedisTemplate操作对应表
redis命令和RedisTemplate操作对应表 redisTemplate.opsForValue();//操作字符串 redisTemplate.opsForHash();//操作hash r ...
- nohup命令重定向标准输出和错误输出
命令:command > /dev/null 2>&1 & 输出到/dev/null表示输出重定向到黑洞,即输出内容不打印到屏幕上,null是/dev下空设备文件. &g ...
- 分享一些好用的 Chrome 插件!
使用浏览器扩展程序可以使你的工作效率提高数倍不止,那么下面我就向大家分享一下我日常使用的扩展,可能大多数扩展大家都已经在使用了,不过也难免有一两个是你不知道的. 以下排名并不分先后,请坚持看到最后,或 ...
- Linux的MySQL安装方法
第一种: APT方式安装 在ubuntu系统的apt软件仓库中,默认存在MySQL数据库 在用户模式下使用命令: apt/apt-get install mysql-server mysql-cli ...
- 与Bat脚本的故事
因为工作时需要将定时处理的业务抽出来,废弃通过监听定时调用的这种方法,改为通过第三方软件定时执行bat脚本来实现,所以学习了一下bat脚本,整理出一些学习中的基础点和重点. 基础点: (1)bat脚本 ...
- 日志分析平台ELK之搜索引擎Elasticsearch集群
一.简介 什么是ELK?ELK是Elasticsearch.Logstash.Kibana这三个软件的首字母缩写:其中elasticsearch是用来做数据的存储和搜索的搜索引擎:logstash是数 ...