C++11标准(4)——并发库(多线程)

欢迎来到博主的专栏:c++杂谈
博主ID:代码小豪

文章目录

    • thread的相关函数
    • thisthread

c++11新增了与并发相关的库,包含线程、以及互斥、同步等与线程安全相关的库,与linux中所使用POSIX库不同,并发库是将其进行了封装,不再是面向过程的使用方式,并且添加了一些c++11的特性,比如右值引用,可变参数模板等。

那么这么做有什么好处呢?第一使用并发库可以跨平台,比如在linux环境下,并发管理使用的pthread相关的函数,在windows下则是Thread。而c++并发库底层是对各个系统的线程库进行封装,所以不同的环境使用并发库的方法是一致的。

下面是linux和windows的线程创建的差异对比

//LINUX pthread库创建线程int pthread_create(pthread_t *tidp,const pthread_attr_t *attr, void *(*start_rtn)(void*), void *arg);// windows线程创建API 
HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,//SDSIZE_T dwStackSize,//initialstacksizeLPTHREAD_START_ROUTINE lpStartAddress,//threadfunctionLPVOID lpParameter,//threadargumentDWORD dwCreationFlags,//creationoptionLPDWORD lpThreadId//threadidentifier)

而关于线程的创建、调度、回收等相关原理,博主在linux开发的专栏详细说明过,因此不再赘述,这里放出相关的链接
线程操作
互斥同步

c++11中与线程相关的库叫做<thread>。而其中线程被封装的类是thread::thread。

thread的相关函数

default (1)	
thread() noexcept;
initialization (2)	
template <class Fn, class... Args>
explicit thread (Fn&& fn, Args&&... args);
copy [deleted] (3)	
thread (const thread&) = delete;
move (4)	
thread (thread&& x) noexcept;

一般情况下使用构造函数2更多,因为其支持可变参数,使用起来更加的灵活,thread::thread不存在拷贝构造函数和赋值重载函数,所以无法将一个thread对象拷贝给另一个thread对象。

这里我们拿构造函数2为例,fn表示未来线程执行的任务,可以是一个函数指针、函数对象、lambda表达式。Args表示执行该任务所需要的参数。

void thread1run(int a, int b) {std::cout <<"thread1 do:"<< a + b << std::endl;
}void thread2run(std::string str) {std::cout <<"thread2 do:" << str << std::endl;
}int main() {std::thread t1(thread1run,30,50);std::thread t2(thread2run,"hello, world");std::thread t3([](std::string str)->void {std::cout << "thread3 do:" << str << std::endl;},"hello,world");t1.join();t2.join();t3.join();return 0;
}
void join();

thread对象一旦创建,就会立即运行,当一个线程执行结束时,需要进行回收,因为线程本质上一种资源,会占用内存当中的空间,如果不进行回收,就会导致内存泄漏。而回收所使用的成员函数为join,比如线程对象t1执行结束,主线程调用t1.join(),就会将t1进行回收,不过前提是t1的任务已经执行完毕,否则主线程将会阻塞等待在t1.join()中,直到t1结束。

既然说一个线程如果执行结束,并不会主动的退出,需要主线程使用join去回收,那么如果我们希望一个线程,他不需要主线程去进行回收,也就是不用去调用join,那么该怎么办呢?答案是使用detach。使用detach后的线程将会是独立的,这意味不需要主线程去join来回收它,当其结束执行后,将会自动退出,不会占用资源。

bool joinable() const noexcept;

如果一个线程对象,需要join来回收,那么这个线程就叫做joinable;如果一个线程对象,不需要join来回收,那么这个线程则是non joinable。成员函数joinable就是用来检测该线程是否是joinable的。若为真,返回true,若为假,则返回false。

那么什么样的线程对象是non joinable的呢?

  • 1、以默认构造函数创建的线程对象。
  • 2、使用detach的独立线程。
  • 3、被join回收后的线程
  • 4、因被move构造而移除的线程
void thread2run(std::string str) {std::cout <<"thread2 do:" << str << std::endl;
}void testjoinable(std::thread& t,std::string t_name) {if (t.joinable()) std::cout << t_name << " is joinable"<<std::endl;else std::cout << t_name << " is non-joinable"<<std::endl;
}int main() {std::thread t1;												//default_constructorstd::thread t2(thread2run,"hello, world");std::thread t3([](std::string str)->void {std::cout << "thread3 do:" << str << std::endl;},"hello,world");t3.detach();testjoinable(t1, "thread1");//thread1 is non-joinabletestjoinable(t2, "thread2");//thread2 is joinabletestjoinable(t3, "thread3");//thread3 is non-joinable//t1.join();t2.join();//t3.join();testjoinable(t2, "thread2");//thread2 is non-joinablereturn 0;
}

要注意,对non_joinable的线程使用join是会导致崩溃的,因此,我们可以在使用join之前,判断一下该线程是否是joinable。

	if(t1.joinable()) t1.join();if(t2.joinable()) t2.join();if(t3.joinable()) t3.join();

thisthread

有没有考虑到一点,线程对象只有在主线程中才能访问到,那么如果我们想要在线程中对该线程进行一些控制,那么该怎么做呢?答案是线程中可以使用namespace thisthread,里面包含了四种函数,注意thisthread中的一切函数,都是只对本线程生效。

thread::id get_id() noexcept;
//获取当前线程的ID
std::thread::id main_thread_id = std::this_thread::get_id(); //主线程 IDvoid is_main_thread() {  //判断是否是主线程的IDstd::cout<<std::this_thread::get_id()<<":" ;
//其他线程使用thisthread::getid,只会获取该线程的IDif (main_thread_id == std::this_thread::get_id())   std::cout << "This is the main thread.\n";elsestd::cout << "This is not the main thread.\n";
}int main()
{is_main_thread();std::thread th(is_main_thread);th.join();
}

线程ID的虽然是一个整数类型,但是在C++中并没有直接用int等整数类型作为线程ID的类型,而是thread::id类型,这是因为不同的平台下对于线程ID有不同的使用标准,所以C++选择统一使用一个类来封装,第二个点就是,线程ID支持在unorder_map等容器中,作为key值索引。

void yield() noexcept;

yield是主动让出当前线程的执⾏权,让其他线程先执行。此函数的确切行为依赖于实现,特别是取
决于使⽤中的OS调度器机制和系统状态。例如,先进先出实时调度器(Linux的SCHED_FIFO)
会挂起当前线程并将它放到准备运⾏的同优先级线程的队列尾。比如下面的案例代码:

std::atomic<bool> ready(false);void count1m(int id) {while (!ready) {             // 等待主进程将ready置为truestd::this_thread::yield();	//如果ready没有为true,那么线程就不会开始计算//因为一直调用yield,切换下一个线程执行,直到ready为true。}for (volatile int i = 0; i < 1000000; ++i) {}std::cout << id;
}int main()
{std::thread threads[10];std::cout << "10个进程比赛谁先算完100万:\n";for (int i = 0; i < 10; ++i) threads[i] = std::thread(count1m, i);ready = true;               // go!for (auto& th : threads) th.join();std::cout << '\n';return 0;
}

最后的两个函数与时间有关。分别是sleep_forsleep_until

template <class Rep, class Period>void sleep_for (const chrono::duration<Rep,Period>& rel_time);template <class Clock, class Duration>void sleep_until (const chrono::time_point<Clock,Duration>& abs_time);

其中参数chrono::duration和chrono::time_point都是<chrono>库中的类,<chrono>库也是c++11新增的库,该库与时间相关,也许未来会给大家带来chrono库的介绍。总之,我们可以把time_point视为一个时间点,duration视为一个时间段。这两者有什么区别呢?时间段表示一个范围的时间,而时间点表示一个具体的时间刻度,比如五分钟后,表示一个时间段,而9:30,表示一个时间点。

sleepfor可以让线程休眠一个时间段。

void sleep10s() {for (int i = 10; i > 0; --i) {std::cout << i << std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));}std::cout << "Lift off!\n";
}int main()
{std::thread t1(count1m);t1.join();return 0;
}

而sleep_until可以让一个线程休眠到一个具体的时间点。比如下面的示例:

#include <chrono>         // std::systemclock
#include <iomanip>        // std::puttimevoid SleepNextMin() {using std::chrono::system_clock;std::time_t tt = system_clock::to_time_t(system_clock::now());//获取当前时间struct std::tm* ptm = std::localtime(&tt);std::cout << "Current time: " << std::put_time(ptm, "%X") << '\n';std::cout << "Waiting for the next minute to begin...\n";++ptm->tm_min; ptm->tm_sec = 0;								//将时间点设置为下一分钟0刻std::this_thread::sleep_until(system_clock::from_time_t(mktime(ptm)));std::cout << std::put_time(ptm, "%X") << " reached!\n";
}int main()
{std::thread t1(SleepNextMin);t1.join();return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/85511.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/85511.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

优化TCP/IP协议栈与网络层

优化TCP/IP协议栈与网络层 在高性能架构中,网络性能往往成为系统吞吐量与响应速度的关键因素之一。而TCP/IP协议栈作为现代互联网通信的核心,其默认配置在高并发场景下常常无法满足大规模分布式系统的性能需求。因此,架构师在构建系统时,有必要对TCP/IP协议栈及其所在的网…

Nginx常见功能

Nginx 是一个高性能的 HTTP 和反向代理服务器&#xff0c;除了基本的 Web 服务功能外&#xff0c;它还支持许多高级功能。以下是 Nginx 常用的一些功能及其设置方法&#xff1a; 1. 反向代理 反向代理是 Nginx 最常用的功能之一&#xff0c;用于将客户端请求转发给后端服务器&a…

UniSAL:用于组织病理学图像分类的统一半监督主动学习方法|文献速递-深度学习医疗AI最新文献

Title 题目 UniSAL: Unified Semi-supervised Active Learning for histopathologicalimage classification UniSAL&#xff1a;用于组织病理学图像分类的统一半监督主动学习方法 01 文献速递介绍 组织病理学图像在癌症筛查、诊断及治疗决策中起着关键作用&#xff0c;有助…

智慧园区建设资料合集(Wordppt原件)

化工园区安全风险智能化管控平台.docx 数字孪生赋能的智慧园区物联网云平台建设方案.pptx 园区智慧安防解决方案.docx 新型智慧园区规划设计方案.pptx 新型智慧园区建设方案.docx 园区大数据治理解决方案.pptx 智慧产业园区综合解决方案.docx 智慧工业园区大数据云平台解决方案…

好玩的镜像汇总

一些镜像仓库汇总 https://github.com/code-lives/Nas-Docker https://github.com/TWO-ICE/Awesome-NAS-Docker image-matting是一款集成了AI大模型的&#xff0c;支持容器化部署的短平快抠图工具 https://mp.weixin.qq.com/s/A1VKAYaDdbCs2o1L4ZYkSw Moodist是一个有助于专注…

langchain 开发实战

我的开发环境是 windows python3.8 langchain版本&#xff1a; # langchain 0.2.17 # langchain-community 0.2.19 # langchain-core 0.2.43 # langchain-openai 0.1.25 # langchain-text-…

粉色 PCB:当电路板染上温柔的科技浪漫

在电子产品的世界里&#xff0c;印制电路板&#xff08;PCB&#xff09;是其核心的 “骨架”&#xff0c;支撑并连接着各种电子元件&#xff0c;让电子产品得以实现复杂的功能。以往&#xff0c;PCB 常见的颜色是绿色&#xff0c;然而&#xff0c;猎板打破常规&#xff0c;推出…

Navicat 技术指引 | TiDB 数据查看器

目前&#xff0c;Navicat 两款工具支持对 TiDB 数据库的管理开发功能&#xff1a;一款是旗舰款 Navicat Premium&#xff0c;另一款是其轻量化功能的 Navicat Premium Lite&#xff08;官方轻量级免费版&#xff09;。Navicat 自版本 17.1 开始支持 TiDB 7。它支持的系统有 Win…

xlsx、xlsx-style 的配置及导出(分享)

文章目录 1. 基础配置1.1 单元格内容1.2 单元格合并、列宽、行高1.3 单元格样式 2. sheet 配置、多个 sheet3. excel 导出4. 数据插入&#xff08;进阶&#xff09; 1. 基础配置 1.1 单元格内容 注&#xff1a;xlsx、xlsx-style 都存在 write 方法&#xff0c;xlsx 设置单元格…

算法第38天|322.零钱兑换\139. 单词拆分

322.零钱兑换 题目 思路与解法 class Solution { public:int coinChange(vector<int>& coins, int amount) {// dp数组&#xff1a;// dp[i]: 凑成总金额i&#xff0c;所需的最少硬币个数vector<int> dp(amount 1, INT_MAX);dp[0] 0;for (int i 0; i &l…

使用MobileNetV3训练水果分类模型并用Flask部署

前言 在计算机视觉领域&#xff0c;图像分类是一个基础且重要的任务。本文将介绍如何使用MobileNetV3预训练模型来训练一个水果分类模型&#xff0c;并通过Flask框架进行部署。MobileNetV3作为轻量级网络&#xff0c;在保持较高精度的同时&#xff0c;具有较快的推理速度&…

SCADA技术与市场全景解析

SCADA系统作为工业自动化领域的关键组成部分&#xff0c;成为了工业企业推进数字化转型的有力抓手。本文将重点介绍SCADA系统的发展历程、系统构成及工作原理、发展趋势&#xff0c;分析其行业应用差异&#xff0c;以及与物联网和组态软件的区别&#xff0c;梳理其在不同行业的…

在线教学课程视频AI智能大纲代码与演示

AI根据视频内容自动生成视频大纲&#xff0c;极大地提高了视频内容的管理效率、可访问性和用户体验&#xff0c;是推动视频内容智能化发展的重要一环。AI根据视频内容自动生成视频内容大纲。这种功能&#xff0c;应用场景有哪些&#xff1f; 应用场景&#xff1a; 在线教育平台…

动态WCMP+Flowlet ALB:双引擎驱动智算网络负载均衡

传统网络架构的困境 在 AI 算力爆发的今天&#xff0c;数据中心网络正经历前所未有的流量形态变革。传统 Clos 网络架构依赖逐流 ECMP 均衡算法&#xff0c;其设计逻辑基于 “大量短流” 假设&#xff0c;通过五元组 HASH 实现负载分担。然而 AI 训练场景呈现出鲜明的流量特征…

软件测试题

选择题 1、导致软件缺陷的原因有很多&#xff0c;①—④是可能的原因&#xff0c;其中最主要的原因包括( )。 ①软件需求说明书编写的不全面&#xff0c;不完整&#xff0c;不准确&#xff0c;而且经常更改 ②软件设计说明书 ③软件操作人员的水平 ④开发人员不能很好的…

微信小程序页面容器弹出层

效果图 .JS Page({data: {show: false,duration: 300,position: right,round: false,overlay: true,customStyle: ,overlayStyle: },popup(e) {const position e.currentTarget.dataset.positionlet customStyle let duration this.data.durationswitch(position) {case to…

Excel批量计算时间差

执行以下操作&#xff0c;将自定义格式代码应用到单元格&#xff1a; 选择相应的单元格。在"开始"选项卡上的"数字"组中&#xff0c;单击"常规"框旁边的箭头&#xff0c;然后单击"其他数字格式"。在"设置单元格格式"对话框…

shell脚本--变量

1.变量是什么 2.变量类型 3.动态&#xff0c;静态&#xff0c;强弱类型 4.变量的命名 5.变量的定义和引用 5.1三种变量类型 普通变量 环境变量 局部变量 5.2单引号&#xff0c;双引号&#xff0c;强弱引用 双引号对变量赋值的影响01:59&#xff1a;给变量加双引号&#x…

大模型Text2SQL之在CentOS上使用yum安装与使用MySQL

前言 学习大模型的时候需要一个mysql&#xff0c;原因还是在公司使用电脑的时候不允许按照Docker-Desktop&#xff0c;我的宿主机其实是MAC&#xff0c;我习惯上还是在centsos上面安装,就发现这件过去很简单的事情居然捣鼓了我蛮久&#xff0c;记录一下。 容器环境 我直接安…

机器人大脑的进化:Physical Intelligence如何用“知识隔离“破解VLA模型三大难题

目录 引言&#xff1a;当GPT遇上机器人手臂 第一章&#xff1a;VLM 与 VLA的介绍 VLM (Vision-Language Model) - 视觉语言模型 VLA (Vision-Language Agent) - 视觉语言智能体 VLM和VLA的对比 第二章&#xff1a;VLA模型的进化史 - 从"口述指挥"到"精确控…