C++ 多线程std::thread以及条件变量和互斥量的使用
前言
- 本文章主要介绍C++11语法中std::thread的使用,以及条件变量和互斥量的使用。
std::thread介绍
构造函数
- std::thread 有4个构造函数
-
// 默认构造函,构造一个线程对象,在这个线程中不执行任何处理动作 thread() noexcept;// 移动构造函数。将 other 的线程所有权转移给新的thread 对象。之后 other 不再表示执行线程。 // 线程对象只可移动,不可复制 thread( thread&& other ) noexcept;// 创建线程对象,并在该线程中执行函数f中的业务逻辑,args是要传递给函数f的参数 template< class F, class... Args > explicit thread( F&& f, Args&&... args );// 使用=delete显示删除拷贝构造, 不允许线程对象之间的拷贝 thread( const thread& ) = delete;
- 通过以下代码演示下如何构造函数的使用
-
#include <iostream>#include <thread>#include <chrono>void threadFunc2() {std::cout << "enter threadFunc2" << std::endl;}void threadFunc3(int data) {std::cout << "enter threadFunc3, data: " << data << std::endl;}class CThread4 {public:void threadFunc4(const char * data) {std::cout << "enter threadFunc4, data: " << data << std::endl;}};void threadFunc5() {for (int i = 0; i < 5; i++) {std::cout << "enter threadFunc5" << std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));}}int main() {// 默认构造std::thread th1;// 线程中执行函数std::thread th2(threadFunc2);th2.join();// 线程中执行带参函数std::thread th3(threadFunc3, 10010);th3.join();CThread4 ct4;// 线程中执行类成员函数std::thread th4(&CThread4::threadFunc4, &ct4, "hello world");th4.join();std::thread th5_1(threadFunc5);// 使用移动构造std::thread th5_2(std::move(th5_1));th5_2.join();// 执行lambda表达式std::thread th6([] {std::cout << "enter threadFunc6" << std::endl;});th6.join();system("pause");return 0;}
-
- 执行结果
-
enter threadFunc2enter threadFunc3, data: 10010enter threadFunc4, data: hello worldenter threadFunc5enter threadFunc5enter threadFunc5enter threadFunc5enter threadFunc5enter threadFunc6请按任意键继续. . .
-
成员函数
-
// 获取线程ID std::thread::id get_id() const noexcept; // 阻塞当前线程,直至调用join的子线程运行结束 void join(); // 将执行线程从线程对象中分离,允许独立执行。 void detach(); // 判断主线程和子线程的关联状态 bool joinable() const noexcept; // 如果 *this 仍然有一个关联的运行中的线程,则调用 std::terminate()。 // 否则,将 other 的状态赋给 *this 并将 other 设置为默认构造的状态。 thread& operator=( thread&& other ) noexcept;
- 通过代码看下如何使用成员函数
-
#include <iostream>#include <thread>#include <chrono>void threadFunc3(int data) {std::cout << "enter threadFunc3, data: " << data << std::endl;}void threadFunc4(int data) {std::cout << "start threadFunc4, data: " << data << std::endl;std::this_thread::sleep_for(std::chrono::seconds(2));std::cout << "end threadFunc4, data: " << data << std::endl;}void threadFunc5(int data) {std::cout << "start threadFunc5, data: " << data << std::endl;std::this_thread::sleep_for(std::chrono::seconds(2));std::cout << "end threadFunc5, data: " << data << std::endl;}int main() {{// 线程中执行带参函数std::thread th3(threadFunc3, 10010);std::cout << "th3 id: " << th3.get_id() << std::endl;// 此刻th3线程与主线程有关联std::cout << "th3 joinable: " << th3.joinable() << std::endl;th3.join();std::cout << "th3 id: " << th3.get_id() << std::endl;// 线程执行结束,此刻th3线程与主线程无关联std::cout << "th3 joinable: " << th3.joinable() << std::endl;}{std::thread th5(threadFunc5, 10050);// 如果不想在主线程中等待子线程,可以使用detach。// 这样即便主线程运行结束,子线程依旧会执行// 实际使用时不建议这样做th5.detach();}system("pause");return 0;}
-
- 执行结果
-
enter threadFunc3, data: 10010th3 id: 12820th3 joinable: 1th3 id: 0th3 joinable: 0start threadFunc5, data: 10050请按任意键继续. . . end threadFunc5, data: 10050
-
条件变量
- 条件变量是C++11提供的一种用于等待的同步机制,它能阻塞一个或多个线程,直到收到另外一个线程发出的通知或者超时时,才会唤醒当前阻塞的线程。
- C++11中的条件变量叫
condition_variable
,需要配合std::unique_lock<std::mutex>
使用。 - 先看以下一段代码
-
#include <iostream>#include <thread>#include <chrono>#include <mutex>#include <condition_variable>int g_cnt = 0;// 定义互斥量std::mutex g_mutex;// 定义条件变量std::condition_variable g_cond;void threadFunc1() {while (g_cnt != 50) {std::this_thread::sleep_for(std::chrono::milliseconds(1));}std::cout << "threadFunc1 g_cnt: " << g_cnt << std::endl;}void threadFunc2() {while (g_cnt < 100) {g_cnt++;std::this_thread::sleep_for(std::chrono::milliseconds(1));}}int main() {{std::thread th1(threadFunc1);std::thread th2(threadFunc2);th1.join();th2.join();std::cout << "g_cnt: " << g_cnt << std::endl;}system("pause");return 0;}
-
- 线程2对
g_cnt
进行递增操作,线程1在g_cnt
等于50时退出循环并打印结果。但这个流程有一个问题,比如线程1某次访问g_cnt时,值为49,下一次再访问时,值可能为50,也可能为51。如果g_cnt
值为51,那这个条件永远都不会满足,循环也就永远无法结束。并且线程1中,我们只想获取g_cnt
等于50这个状态,没必要每次都去访问,这也会耗费系统资源。 - 使用条件变量做以下修改
-
#include <iostream>#include <thread>#include <chrono>#include <mutex>#include <condition_variable>int g_cnt = 0;// 定义互斥量std::mutex g_mutex;// 定义条件变量std::condition_variable g_cond;bool g_flag = false;void threadFunc1() {std::unique_lock<std::mutex> lock(g_mutex);while (!g_flag) {// 阻塞等待,等待被唤醒g_cond.wait(lock);}std::cout << "threadFunc1 g_cnt: " << g_cnt << std::endl;}void threadFunc2() {while (g_cnt < 100) {g_cnt++;if (g_cnt == 50) {g_flag = true;// 唤醒阻塞的线程g_cond.notify_one();}std::this_thread::sleep_for(std::chrono::milliseconds(1));}}int main() {{std::thread th1(threadFunc1);std::thread th2(threadFunc2);th1.join();th2.join();std::cout << "g_cnt: " << g_cnt << std::endl;}system("pause");return 0;}
-
- 这样就可以保证线程1的条件肯定可以满足。
线程互斥
- 控制线程对共享资源的访问。比如写文件时,不能读文件,读文件时,不能写文件。
- C++11提供了4种互斥锁
std::mutex
:独占的互斥锁,不能递归使用。std::timed_mutex
:带超时的独占互斥锁,不能递归使用。在获取互斥锁资源时增加了超时等待功能。std::recursive_mutex
:递归互斥锁,不带超时功能。允许同一线程多次获得互斥锁。std::recursive_timed_mutex
:带超时的递归互斥锁。
- 分析以下这段代码的输出结果
-
#include <iostream>#include <thread>#include <chrono>#include <mutex>int g_cnt = 0;void threadFunc(int num) {for (int i = 0; i < num; i++) {g_cnt++;std::this_thread::sleep_for(std::chrono::milliseconds(1));}}int main() {{// 线程中执行带参函数std::thread th1(threadFunc, 100);std::thread th2(threadFunc, 100);th1.join();th2.join();std::cout << "g_cnt: " << g_cnt << std::endl;}system("pause");return 0;}
-
- 我们期望的
g_cnt
输出结果为200
,但实际上g_cnt
很大概率不是200
而是小于200
。这是由于没有对共享资源g_cnt
进行加锁保护,这会导致数据竞争。两个线程可能同时访问g_cnt
,导致某个线程的++
操作被另一个线程覆盖。 - 对上面代码做下修改,对共享资源
g_cnt
进行加锁保护。-
#include <iostream>#include <thread>#include <chrono>#include <mutex>int g_cnt = 0;// 定义互斥量std::mutex g_mutex;void threadFunc(int num) {for (int i = 0; i < num; i++) {// 加锁g_mutex.lock();g_cnt++;// 解锁g_mutex.unlock();std::this_thread::sleep_for(std::chrono::milliseconds(1));}}int main() {{// 线程中执行带参函数std::thread th1(threadFunc, 100);std::thread th2(threadFunc, 100);th1.join();th2.join();std::cout << "g_cnt: " << g_cnt << std::endl;}system("pause");return 0;}
-
- 加锁后,就可以保证
g_cnt
的结果为200。 - 上面代码有这样一种风险。如果加锁后,中途退出而忘记解锁,就会导致死锁现象。
-
void threadFunc(int num) {for (int i = 0; i < num; i++) {// 加锁g_mutex.lock();g_cnt++;if (i == 50) {break;}// 解锁g_mutex.unlock();std::this_thread::sleep_for(std::chrono::milliseconds(1));}}
-
- C++ 11 提供了一种模板类
std::lock_guard
,可以简化互斥锁的写法。调用构造时加锁,离开作用域时解锁。不用手动加解锁,大大提高了安全性。 - 实现代码如下。即便中途退出,也不会出现死锁现象。
-
void threadFunc(int num) {for (int i = 0; i < num; i++) {// 加锁std::lock_guard<std::mutex> lock(g_mutex);g_cnt++;if (i == 50) {break;}std::this_thread::sleep_for(std::chrono::milliseconds(1));}}
-
参考
- https://en.cppreference.com/w/cpp/thread/thread
相关文章:
C++ 多线程std::thread以及条件变量和互斥量的使用
前言 本文章主要介绍C11语法中std::thread的使用,以及条件变量和互斥量的使用。 std::thread介绍 构造函数 std::thread 有4个构造函数 // 默认构造函,构造一个线程对象,在这个线程中不执行任何处理动作 thread() noexcept;// 移动构造函…...
新华三H3CNE网络工程师认证—子接口技术
子接口(subinterface)是通过协议和技术将一个物理接口(interface)虚拟出来的多个逻辑接口。在VLAN虚拟局域网中,通常是一个物理接口对应一个 VLAN。在多个 VLAN 的网络上,无法使用单台路由器的一个物理接口…...
【MySQL】InnoDB内存结构
目录 InnoDB内存结构 主要组成 缓冲池 缓冲池的作用 缓冲池的结构 缓冲池中页与页之间连接方式分析 缓冲池如何组织数据 控制块初始化 页面初始化 缓冲池中页的管理 缓冲区淘汰策略 查看缓冲池信息 总结 变更缓冲区-Chang Buffer 变更缓冲区的作用 主要配置选项…...
基于大数据爬虫数据挖掘技术+Python的网络用户购物行为分析与可视化平台(源码+论文+PPT+部署文档教程等)
#1024程序员节|征文# 博主介绍:CSDN毕设辅导第一人、全网粉丝50W,csdn特邀作者、博客专家、腾讯云社区合作讲师、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老…...
蓝桥杯每日真题 - 第19天
题目:(费用报销) 题目描述(13届 C&C B组F题) 解题思路: 1. 问题抽象 本问题可以看作一个限制条件较多的优化问题,核心是如何在金额和时间约束下选择最优方案: 动态规划是理想…...
CentOS7.9.2009的yum更换vault地窖保险库过期源,epel的archive归档源 笔记241117
CentOS7.9.2009的yum更换vault地窖保险库过期源,epel的archive归档源 笔记241117 备份 /etc/yum.repos.d 文件夹 tempUri/etc/yum.repos.d ; sudo cp -a $tempUri $tempUri.$(date %0y%0m%0d%0H%0M%0Sns%0N).bak清空 /etc/yum.repos.d 文件夹 sudo rm -rf /etc…...
Spark SQL大数据分析快速上手-完全分布模式安装
【图书介绍】《Spark SQL大数据分析快速上手》-CSDN博客 《Spark SQL大数据分析快速上手》【摘要 书评 试读】- 京东图书 大数据与数据分析_夏天又到了的博客-CSDN博客 Hadoop完全分布式环境搭建步骤-CSDN博客,前置环境安装参看此博文 完全分布模式也叫集群模式。将Spark目…...
Java面试题2024-Java基础
Java基础 1、 Java语言有哪些特点 1、简单易学、有丰富的类库 2、面向对象(Java最重要的特性,让程序耦合度更低,内聚性更高) 3、与平台无关性(JVM是Java跨平台使用的根本) 4、可靠安全 5、支持多线程 2、…...
局域网协同办公软件,2024安全的协同办公软件推荐
在2024年,随着数字化转型的深入和远程工作需求的增加,协同办公软件已成为企业提升工作效率、优化沟通流程的重要工具。 以下是一些值得推荐的安全的协同办公软件: 钉钉 功能全面:钉钉是一款综合性极强的企业级协同软件ÿ…...
osg、osgearth简介及学习环境准备
一、osg简介(三维场景图渲染与调度引擎) OSG是Open Scene Graphic 的缩写,OSG于1997年诞生于以为滑翔机爱好者之手,Don burns 为了对滑翔机的飞行进行模拟,对openGL的库进行了封装,osg的雏形就这样诞生了&…...
nodejs基于微信小程序的云校园的设计与实现
摘 要 相比于传统的校园管理方式,智能化的管理方式可以大幅提高校园的管理效率,实现了云校园管理的标准化、制度化、程序化的管理,有效地防止了云校园信息的不规范管理,提高了信息的处理速度和精确度,能够及时、准确地…...
uni-app快速入门(十)--常用内置组件(下)
本文介绍uni-app的textarea多行文本框组件、web-view组件、image图片组件、switch开关组件、audio音频组件、video视频组件。 一、textarea多行文本框组件 textarea组件在HTML 中相信大家非常熟悉,组件的官方介绍见: textarea | uni-app官网uni-app,un…...
golang基础
在 Go 中字符串是不可变的,例如下面的代码编译时会报错: cannot assign to s[0] 但如果真的想要修改怎么办呢?下面的代码可以实现: var s string "hello" s [ 0 ] c s : "hello" c : [] b…...
Selenium + 数据驱动测试:从入门到实战!
引言 在软件测试中,测试数据的多样性和灵活性对测试覆盖率至关重要。而数据驱动测试(Data-Driven Testing)通过将测试逻辑与数据分离,极大地提高了测试用例的可维护性和可扩展性。本文将结合Selenium这一流行的测试工具࿰…...
LLaMA与ChatGLM选用比较
目录 1. 开发背景 2. 目标与应用 3. 训练数据 4. 模型架构与规模 5. 开源与社区支持 6. 对话能力 7. 微调与应用 8. 推理速度与资源消耗 总结 LLaMA(Large Language Model Meta AI)和 ChatGLM(Chat Generative Language Model)都是强大的大型语言模型,但它们有一…...
GPTZero:高效识别AI生成文本,保障学术诚信与内容原创性
产品描述 GPTZero 是一款先进的AI文本检测工具,专为识别由大型语言模型(如ChatGPT、GPT-4、Bard等)生成的文本而设计。它通过分析文本的复杂性和一致性,判断文本是否可能由人类编写。GPTZero 已经得到了超过100家媒体机构的报道&…...
C/C++ 优化,strlen 示例
目录 C/C optimization, the strlen examplehttps://hallowed-blinker-3ca.notion.site/C-C-optimization-the-strlen-example-108719425da080338d94c79add2bb372 揭开优化的神秘面纱... 让我们来谈谈 CPU 等等,SIMD 是什么? 为什么 strlen 是一个很…...
【动手学深度学习Pytorch】1. 线性回归代码
零实现 导入所需要的包: # %matplotlib inline import random import torch from d2l import torch as d2l import matplotlib.pyplot as plt import matplotlib import os构造人造数据集:假设w[2, -3.4],b4.2,存在随机噪音&…...
深入理解PyTorch中的卷积层:工作原理、参数解析与实际应用示例
深入理解PyTorch中的卷积层:工作原理、参数解析与实际应用示例 在PyTorch中,卷积层是构建卷积神经网络(CNNs)的基本单元,广泛用于处理图像和视频中的特征提取任务。通过卷积操作,网络可以有效地学习输入数…...
DataGear 5.2.0 发布,数据可视化分析平台
DataGear 企业版 1.3.0 已发布,欢迎体验! http://datagear.tech/pro/ DataGear 5.2.0 发布,图表插件支持定义依赖库、严重 BUG 修复、功能改进、安全增强,具体更新内容如下: 重构:各模块管理功能访问路径…...
uniapp: vite配置rollup-plugin-visualizer进行小程序依赖可视化分析减少vender.js大小
一、前言 在之前文章《uniapp: 微信小程序包体积超过2M的优化方法(主包从2.7M优化到1.5M以内)》中,提到了6种优化小程序包体积的方法,但并没有涉及如何分析common/vender.js这个文件的优化,而这个文件的大小通常情况下…...
深度学习:如何复现神经网络
深度学习:如何复现神经网络 要复现图中展示的卷积神经网络(CNN),我们需详细了解和配置每层网络的功能与设计理由。以下将具体解释各层的配置以及设计选择的原因,确保网络设计的合理性与有效性。 详细的网络层配置与设…...
Spring Boot与MyBatis-Plus的高效集成
Spring Boot与MyBatis-Plus的高效集成 引言 在现代 Java 开发中,MyBatis-Plus 作为 MyBatis 的增强工具,以其简化 CRUD 操作和无需编写 XML 映射文件的特点,受到了开发者的青睐。本篇文章将带你一步步整合 Spring Boot 与 MyBatis-Plus&…...
【Unity ShaderGraph实现流体效果之Function入门】
Unity ShaderGraph实现流体效果之Node入门(一) 前言Shader Graph NodePosition NodeSplit NodeSubtract NodeBranch Node 总结 前言 Unity 提供的Shader Graph在很大程度上简化了开发者对于编写Shader的工作,只需要拖拽即可完成一个视觉效果…...
Spark RDD sortBy算子执行时进行数据 “采样”是什么意思?
一、sortBy 和 RangePartitioner sortBy 在 Spark 中会在执行排序时采用 rangePartitioner 进行分区,这会影响数据的分区方式,并且这一步骤是通过对数据进行 “采样” 来计算分区的范围。不过,重要的是,sortBy 本身仍然是一个 tr…...
React-useRef与DOM操作
#题引:我认为跟着官方文档学习不会走歪路 ref使用 组件重新渲染时,react组件函数里的代码会重新执行,返回新的JSX,当你希望组件“记住”某些信息,但又不想让这些信息触发新的渲染时,你可以使用ref&#x…...
Mistral AI 发布 Pixtral Large 模型:多模态时代的开源先锋
Mistral AI 最新推出的 Pixtral Large 模型,带来了更强的多模态能力。作为一款开源的多模态模型,它不仅在参数量上达到 1240 亿,更在文本和图像理解上实现了质的飞跃。 模型亮点 1. 多模态能力再升级 Pixtral Large 配备了 123B 参数的解码器…...
Windows、Linux多系统共享蓝牙设备
Windows、Linux多系统共享蓝牙设备 近来遇到一个新问题,就是双系统共享蓝牙鼠标。因为一直喜欢在Windows、Linux双系统之间来回切换,而每次切换系统蓝牙就必须重新配对,当然,通过网络成功解决了问题。 通过这个问题,稍…...
C语言 | Leetcode C语言题解之第564题寻找最近的回文数
题目: 题解: #define MAX_STR_LEN 32 typedef unsigned long long ULL;void reverseStr(char * str) {int n strlen(str);for (int l 0, r n-1; l < r; l, r--) {char c str[l];str[l] str[r];str[r] c;} }ULL * getCandidates(const char * n…...
wsl虚拟机中的dockers容器访问不了物理主机
1 首先保证wsl虚拟机能够访问宿主机IP地址,wsl虚拟机通过vEthernet (WSL)的地址访问,着意味着容器也要通过此IP地址访问物理主机。 2 遇到的问题:wsl虚拟机中安装了docker,用在用到docker容器内的开发环境,但是虚拟机…...
如何拥有自己的私人网站平台/app推广方式有哪些
Orcad 画原理图的快捷键与allegro pcb不同,Orcad是无法修改快捷键的,只能使用系统自带的常用快捷键 I:放大原理图 O:缩小原理图 R:90度旋转 H:水平翻转...
全屏类网站/最有效的网络推广方式和策略
在Windows 8 的IIS(8.0)中搭建PHP运行环境: 一:安装IIS服务器 1.进入控制面板>>程序和功能>>打开或关闭Windows 功能,找到Internet信息服务,记得选中CGI这一项 2.安装完成后在浏览器中打开loca…...
生鲜配送网站建设/艾瑞指数
转载:http://www.jb51.net/LINUXjishu/152017.html 1.命令格式: free [参数] 2.命令功能: free 命令显示系统使用和空闲的内存情况,包括物理内存、交互区内存(swap)和内核缓冲区内存。共享内存将被忽略 3&a…...
1g网站空间/活动推广软文范例
目录一 关联github远程仓库二 关联gitee远程仓库三 origin四 https和git协议五 切换协议六 更新远程仓库到本地1 git fetch2 git pull七 git fetch和git pull的区别1 git fetch2 git pull八 submodule子模块1 背景2 添加子模块3 子模块的使用4 子模块的更新5 删除子模块九 遇到…...
网站搭建服务/百度站长平台链接
云原生技术的不断普及,不仅让使用Kubernetes部署应用成为了当下最主流的方式,而且标志着众多企业迈入了多集群时代。随着集群数量的不断增长,企业在集群管理和运维方面也迎来了诸如集群配置重复劳动、维护管理繁琐等等问题和挑战。01 多集群生…...