c++20 jthread的理解

news/2024/11/16 14:35:01/

c++20 jthread

jthread是c++20所支持的新的线程类型,jthread = joinable thread, 即可以自动join的线程。我们知道在c++11之后,c++标准库开始支持多线程编程,那么thread和jthread之间有何区别,本文将进行重点讲解。

c++11 thread

c++11中thread对象如果在销毁之前处于可join的状态,却没有join的话,将会引发一个异常, 例如下面的例子:

#include <iostream>
#include <thread>int main(){std::cout << std::endl;std::cout << std::boolalpha;std::thread thr{[]{ std::cout << "Joinable std::thread" << std::endl; }};std::cout << "thr.joinable(): " << thr.joinable() << std::endl;std::cout << std::endl;}

点击在线运行

在effective modern c++的37条曾经提到过,使std::thread在所有路径最后都不可结合。而这样的需求就很容易让人想起RAII。

class ThreadRAII {
public:enum class DtorAction { join, detach };     //enum class的信息见条款10ThreadRAII(std::thread&& t, DtorAction a)   //析构函数中对t实行a动作: action(a), t(std::move(t)) {}~ThreadRAII(){                                           //可结合性测试见下if (t.joinable()) {if (action == DtorAction::join) {t.join();} else {t.detach();}}}std::thread& get() { return t; }            //见下private:DtorAction action;std::thread t;
};

这样看起来似乎万事大吉,但是也是有弊端的。effective modern c++ Item39表明了使用ThreadRAII来保证在std::thread的析构时执行join有时不仅可能导致程序表现异常,还可能导致程序挂起,例如下面的例子,thread析构时,程序陷入死锁状态。

#include <iostream>
#include <thread>
#include <future>std::promise<void> p;class ThreadRAII {
public:enum class DtorAction { join, detach };     //enum class的信息见条款10ThreadRAII(std::thread&& t, DtorAction a)   //析构函数中对t实行a动作: action(a), t(std::move(t)) {}~ThreadRAII(){std::cout << "call ~ThreadRAII" << std::endl;//可结合性测试见下if (t.joinable()) {if (action == DtorAction::join) {t.join();} else {t.detach();}}}std::thread& get() { return t; }            //见下private:DtorAction action;std::thread t;
};void react()
{}void detect()
{ThreadRAII tr(std::thread([]{p.get_future().wait();react();}),ThreadRAII::DtorAction::join);throw 1;//这里抛出了异常p.set_value();
}int main()
{try{detect();}catch(...){}
}

解决方案是此类程序应该和异步执行的lambda通信,告诉它不需要执行了,可以直接返回,但是C++11中不支持可中断线程(interruptible threads)。

结合上述的例子,jthread的由来就很好理解了,jthread除了拥有std::thread的行为外,主要增加了以下两个功能:

  • jthread 对象被析构时,会自动调用join,等待其所表示的执行流结束。
  • jthread支持外部请求中止(通过 get_stop_source、get_stop_token 和 request_stop )。

这两个特点和上面的例子是相呼应的。

jthread

了解了jthread产生的原因,下面将介绍jthread的使用方法。

回顾上节的第一个案例,joinable的std::thread如果析构的时候没有join,将会导致异常退出。

#include <iostream>
#include <thread>int main(){std::cout << std::endl;std::cout << std::boolalpha;std::thread thr{[]{ std::cout << "Joinable std::thread" << std::endl; }};std::cout << "thr.joinable(): " << thr.joinable() << std::endl;std::cout << std::endl;
}

将其改成jthread,再次运行,不再会异常退出。

#include <iostream>
#include <thread>int main(){std::cout << std::endl;std::cout << std::boolalpha;std::jthread thr{[]{ std::cout << "Joinable std::thread" << std::endl; }};std::cout << "thr.joinable(): " << thr.joinable() << std::endl;std::cout << std::endl;
}

点击在线运行

下面的这个例子是使用request_stop向线程发出停止运行的请求。注意lambda函数的入参中需要添加std::stop_token类型的入参, 并且需要在线程中添加检查stop_token的代码。

#include <thread>
#include <iostream>using namespace std::chrono_literals;void test_jthread01() {std::jthread jt{ [](std::stop_token stoken) {while (!stoken.stop_requested()) { std::cout << "Doing work\n";std::this_thread::sleep_for(1s);}}};sleep(5);jt.request_stop(); // 请求线程停止,因有响应停止请求而终止线程jt.join();
}
int main()
{test_jthread01();
}

上面我们提到过,使用ThreadRAII来保证在std::thread的析构时执行join有时不仅可能导致程序表现异常,还可能导致程序挂起。

jthread同样可以解决上述问题,jthread对象在析构时会调用request_stop,jthread的析构函数伪代码可能是下面这样的:

~jthread() {if(joinable()) {request_stop(); //More on stop request below.join();}
}

而在线程的内部可以使用std::condition_variable_any去进行配合。condition_variable_any和一样条件变量不同的是, 其wait时会接受一个stop_token,当收到request_stop时,wait会直接返回,不再进行等待。下面是condition_variable_any进行wait时的伪代码:

template<class Lock, class Predicate>
bool wait(Lock& lock, std::stop_token stoken, Predicate stop_waiting){while (!stoken.stop_requested()) {if (stop_waiting()) return true;wait(lock);}return stop_waiting();
}

结合jthread和condition_variable_any就可以很好的解决上述问题,官网对于jthread和condition_variable_any结合的描述如下所示:

If the request_stop() does issue a stop request (i.e., returns true), then all condition variables of base type std::condition_variable_any registered with an interruptible wait for std::stop_tokens associated with the jthread’s internal stop-state will be awoken.

意思是说如果发出了request_stop,那么condition_variable_any类型的条件变量将会唤醒。下面是一个使用jthread和condition_variable_any的完整例子,jthread对象析构时,不再会陷入无穷的等待中。


#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
#include <condition_variable>using namespace std::chrono_literals;
std::condition_variable_any cond;int main()
{std::jthread waiting_worker([](std::stop_token stoken) {std::mutex mutex;std::unique_lock lock(mutex);std::cout << "wait" << std::endl;cond.wait(lock, stoken,[] { return false; });if (stoken.stop_requested()) {std::cout << "Waiting worker is requested to stop\n";return;}});std::this_thread::sleep_for(100s);std::cout << "destroy jthread object, and call request_stop" << std::endl;// Or automatically using RAII:// waiting_worker's destructor will call request_stop()// and join the thread automatically.
}

总结

jthread扩充了std::thread的功能,主要增加了以下两个功能:

  • jthread 对象被析构时,会自动调用join,等待其所表示的执行流结束。
  • jthread支持外部请求中止(通过 get_stop_source、get_stop_token 和 request_stop )。

http://www.ppmy.cn/news/247753.html

相关文章

17 张程序员壁纸推荐

17 张程序员壁纸推荐&#xff0c;喜欢的可以点击图片或指定链接下载高清图。 1、三思后再写代码&#xff01;&#xff01;&#xff01; 2、从世界上搜索喜欢你的人&#xff01;&#xff01;&#xff01; 其他分辨率下载&#xff1a; 1920x1080 1920x1200 2560x1440 2560x1600 …

驱动精灵的护眼大师屏保非常不错

打开后是下面的这个程序。 有时候安装好驱动精灵时&#xff0c;百宝箱里没有护眼大师。我的做法是把这个护眼大师的khealtheye文件夹放到自己的电脑上&#xff0c;然后再看百宝箱里就有护眼 大师了。 khealtheye的东西链接 https://download.csdn.net/download/moonlightpeng/…

win10黑色护眼风格一波带走

前两天房间灯泡坏了&#xff0c;一到晚上黑灯瞎火&#xff0c;不过敲代码也没必要说无灯不可&#xff0c;idea或eclipes都有黑暗的风格&#xff0c;切换下就好了。 一分钟后我就后悔了&#xff0c;想查看资源管理器&#xff0c;WinE按一那一瞬间&#xff0c;那惨白的颜色能把我…

2023尚上优选-社区团购 优选电商Spring Cloud Alibaba

尚上优选2023最新企业级微服务架构项目 分布式微服务后端VUE、小程序 尚上优选是真实居住社区内居民团体的一种互联网线上线下购物消费行为&#xff0c;是依托真实社区的一种区域化、小众化、本地化、网络化的团购形式。简而言之&#xff0c;它是依托社区和团长社交关系实现生…

《C++高级编程》读书笔记(三:编码风格)

1、参考引用 C高级编程&#xff08;第4版&#xff0c;C17标准&#xff09;马克葛瑞格尔 2、建议先看《21天学通C》 这本书入门&#xff0c;笔记链接如下 21天学通C读书笔记&#xff08;文章链接汇总&#xff09; 1. 为代码编写文档 在编程环境下&#xff0c;文档通常指源文件中…

问一下ChatGPT如何学习开发iOS应用程序

学习开发iOS应用程序需要以下几个步骤&#xff1a; 学习Swift编程语言或Objective-C编程语言 学习基本的iOS应用程序框架和库&#xff0c;例如UIKit、Core Data和Foundation 学习Xcode IDE和iOS开发工具 开始编写简单的iOS应用程序&#xff0c;并逐渐提高难度 以下是一些推…

java文字版格斗游戏

javabean类 package test2;import java.util.Random;public class Role {private String name;private int blood;private char gender;private String face;String[] boyfaces {"风流优雅", "气宇轩昂", "相貌英俊", "五官端正", &…

java文字格斗游戏

文字格斗游戏 创建Role(人物属性方法&#xff0c;格斗台词和伤害属性的描述) public class Role {private String name;private int hp;private char gander;private String standIn;String[] boyStandIn {"白金之星","皇帝","恋人","愚者…