C++:thread | condition_variable|mutex

欢迎来到 破晓的历程的 博客

⛺️不负时光,不负己✈️

文章目录

    • 引言
    • thread
      • 创建线程
      • 传递参数给线程函数
    • mutex
      • mutex常见用法
    • condition_variable:条件变量
    • 生产消费模型

引言

相信大家在Linux系统编程中都接触过线程创建和退出的相关系统调用,这些系统调用是Linux环境下的一套线程设计方案。但是这种设计方案仅限于Linux环境下使用,其缺点就是可移植性差。所以C++设计了thread库,该库可以适用于任何平台下,从根本上解决了可移植性差的问题。

thread

要使用 std::thread,首先需要包含头文件

#include<thread>

创建线程

可以通过 std::thread 类的构造函数来创建一个线程。构造函数接受一个可调用对象(如函数指针、函数对象、lambda 表达式等)作为参数。线程创建好之后,会自动运行所绑定的函数。

void threadFunction()
{cout << "hello 函数指针" << endl;
}
int main()
{//传入lambda表达式thread t1([](int x = 10) {{cout << "hello lambda表达式" << endl;}});//传入函数指针thread t2(threadFunction);//传入函数对象function<void()>func = threadFunction;thread t3(func);//进行线程等待t1.join();t2.join();t3.join();
}

线程创建好之后,要进行线程等待「调用其内部的join方法」或者进行线程分离「调用其内部的detach方法」
线程等待
主线程要等待新线程全部运行完毕,主线程才能退出,所以要进行线程分离。

thread t(绑定函数)
t.join()

线程分离
线程分离是指将一个线程从主线程中分离出来,使其能够独立运行。当一个线程被设置为分离状态时,它结束时系统会自动回收其资源,而不需要主线程使用join函数来等待其结束并手动回收资源。

线程被分离后,该线程和创建它的线程「例如主线程」之间任何关系,创建它的线程进行退出时,也不会检查被分离线程是否运行完成,

thread t(绑定函数)
//线程分离
t.detach()

传递参数给线程函数

线程函数可以接受参数,这些参数在创建线程时传递给 std::thread 的构造函数。
来看如下示例

#include <iostream>  
#include <thread>  // 一个接受参数的线程函数  
void threadFunction(int x, const std::string& str) {  std::cout << "x = " << x << ", str = " << str << std::endl;  
}  int main() {  int x = 42;  std::string str = "Hello from thread";  // 创建一个线程,传递参数  std::thread t(threadFunction, x, str);  // 等待线程完成  t.join();  return 0;  
}

mutex

在Linux环境下,有这样几个内核暴露出来的系统调用接口:

 #include <pthread.h>int pthread_mutex_lock(pthread_mutex_t *mutex);int pthread_mutex_trylock(pthread_mutex_t *mutex);int pthread_mutex_unlock(pthread_mutex_t *mutex);

这些接口组成了锁的概念,但是由于这些接口仅可以在Linux环境下使用,可移植性较差。C++在这些系统调用接口的基础上,封装出了mutex类。
在C++中,mutex(互斥量)是一种同步机制,用于防止多个线程同时访问共享资源,从而避免数据竞争和条件竞争等问题。它是C++11标准库引入的一部分,位于头文件中。通过使用mutex,开发者可以确保在任何时刻只有一个线程能够访问特定的代码段或资源。

mutex常见用法

定义和初始化

#include<mutex>
#include<iostream>
int main()
{std::mutex mtx;
}

加锁和解锁
访问被保护的资源「临界资源」,必须先获得锁的拥有权。线程必须先锁定mutex,这可以通过调用lock()成员函数实现。一旦完成资源访问,线程应该调用unlock()来释放mutex。

mtx.lock();
//被保护的临界资源
mtx.unlock();

使用std::lock_guard
为了避免忘记解锁或在异常发生时未能解锁,C++提供了std::lock_guard。它是一个简单的RAII(Resource Acquisition Is Initialization)包装器,它在构造时锁定mutex,在析构时自动解锁。

#include <mutex>  std::mutex mtx;  void threadSafeFunction() {  std::lock_guard<std::mutex> lock(mtx);  // 访问受保护的资源  
}

使用std::unique_lock

std::unique_lock提供了比std::lock_guard更多的灵活性。除了自动管理mutex的锁定和解锁外,它还允许延迟锁定、提前解锁、重新锁定等操作。

#include <mutex>  std::mutex mtx;  void threadSafeFunction() {  std::unique_lock<std::mutex> lock(mtx);  // 可以在这里进行延迟锁定、提前解锁等操作  // 访问受保护的资源  
}

假设现在我们要设计一个抢票的程序:有三个窗口和100张票,我们应该如何设计呢?
我们可以这样设计:

mutex mtx;
int ticketaCount = 100;
void threadFunction(int index)
{while (ticketaCount > 0){{lock_guard<std::mutex> guard(mtx);if (ticketaCount > 0){cout << index << " : " << ticketaCount << endl;ticketaCount--;}std::this_thread::sleep_for(std::chrono::milliseconds(100));}}
}
int main()
{vector<thread> poll;for (int i = 1; i <= 3; i++){poll.push_back(thread(threadFunction, i));}for (auto &it : poll){it.join();}
}

tips: 锁的力度越小越好,因为我越小发生错误的概率越低。

condition_variable:条件变量

在Linux环境中,内核暴露给用户一些接口,用于环境变量相关的操作,如下:

       #include <pthread.h>int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);

C++语言层面对其进行了封装,但其背后使用的还是不同的操作系统提供的系统调用的接口,同时也使其拥有了较强的可移植性。
以下是一些C++中std::condition_variable相关函数的使用范例:
1. std::condition_variable::wait
这个函数用于阻塞当前线程,直到条件变量被另一个线程唤醒。它通常与std::unique_lock std::mutex一起使用。

#include <iostream>  
#include <thread>  
#include <mutex>  
#include <condition_variable>  std::mutex m;  
std::condition_variable cond_var;  
bool ready = false;  void worker_thread() {  std::unique_lock<std::mutex> lock(m);  std::cout << "worker_thread() wait\n";  cond_var.wait(lock); // 等待条件变量被唤醒  std::cout << "worker_thread() is processing data\n";  
}  int main() {  std::thread worker(worker_thread);  std::this_thread::sleep_for(std::chrono::milliseconds(5)); // 模拟一些延迟  std::cout << "main() notify_one\n";  cond_var.notify_one(); // 唤醒一个等待的线程  worker.join();  std::cout << "main() end\n";  return 0;  
}

2. std::condition_variable::notify_one 和 std::condition_variable::notify_all
这两个函数用于唤醒等待条件变量的线程。notify_one唤醒一个等待的线程,而notify_all唤醒所有等待的线程。
范例(使用notify_all):

#include <iostream>  
#include <thread>  
#include <mutex>  
#include <condition_variable>  std::mutex m;  
std::condition_variable cond_var;  
bool ready = false;  void print_id(int id) {  std::unique_lock<std::mutex> lock(m);  while (!ready) {  cond_var.wait(lock); // 等待条件变量被唤醒  }  std::cout << "thread " << id << '\n';  
}  void go() {  std::unique_lock<std::mutex> lock(m);  ready = true;  cond_var.notify_all(); // 唤醒所有等待的线程  
}  int main() {  std::thread threads[5];  for (int i = 0; i < 5; ++i) {  threads[i] = std::thread(print_id, i);  }  std::cout << "5 threads ready to race...\n";  go();  for (auto& th : threads) {  th.join();  }  return 0;  
}

3. std::condition_variable::wait_for
这个函数用于在一定时间内等待条件变量被唤醒。如果指定时间内条件变量没有被唤醒,则返回超时状态。

#include <iostream>  
#include <thread>  
#include <mutex>  
#include <condition_variable>  
#include <chrono>  std::mutex mtx;  
std::condition_variable cv;  
bool ready = false;  void worker() {  std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟工作  {  std::lock_guard<std::mutex> lock(mtx);  ready = true;  }  cv.notify_one(); // 通知等待的线程  
}  int main() {  std::thread t(worker);  std::unique_lock<std::mutex> lock(mtx);  if (cv.wait_for(lock, std::chrono::seconds(2), [] { return ready; })) {  std::cout << "Worker线程已完成工作。\n";  } else {  std::cout << "等待超时。\n";  }  t.join();  return 0;  
}

生产消费模型

mutex mtx;
condition_variable cv;
class Queue
{
public:void put(int i){unique_lock<std::mutex>lck(mtx);while (!que.empty()){//这时,生产者应通知消费者去消费,消费完了,再继续生产//生产者进入等待状态,要将锁给释放,并且将自身进入等待状态。cv.wait(lck);}que.push(i);//通知其他一个线程,我生产完了 你们赶快去消费去吧//其他线程得到该通知,就会从等待状态-->阻塞状态--->获取互斥锁才能继续之星cv.notify_all();cout << "生产者 生产:" << i << "号商品" << endl;}int get(){unique_lock<std::mutex>lck(mtx);		while (que.empty()){//消费线程发现:que是空的,通知生产线程。cv.wait(lck);}int q = que.front();que.pop();cv.notify_all();cout << "消费者 消费:" << q << "号商品" << endl;//通知其他生产线程进行生产:return q; }
private:queue<int> que;
};
void Productor(Queue* que)
{for (int i = 0; i < 10; i++){que->put(i);std::this_thread::sleep_for(std::chrono::seconds(2));}
}
void Consumer(Queue* que)
{for (int i = 0; i < 10; i++){que->get();std::this_thread::sleep_for(std::chrono::seconds(2));}
}
int main()
{Queue que;thread productor(Productor, &que);thread consumer(Consumer, &que);productor.join();consumer.join();
}

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

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

相关文章

【学习笔记】一种使用多项式快速计算 sin 和 cos 近似值的方法

一种使用多项式快速计算 sin 和 cos 近似值的方法 在嵌入式开发、游戏开发或其他需要快速数学计算的领域&#xff0c;sin 和 cos 函数的计算时间可能会影响程序的整体性能。特别是在对时间敏感、精度要求不高的场景中&#xff0c;传统的 sin 和 cos 函数由于依赖复杂的数值方法…

RHCSA的学习(4)

一、vi编辑器 &#xff08;1&#xff09;为什么学vi&#xff1f; 所有的Unix Like 系统都会内建 vi 文本编辑器&#xff0c;其他的文本编辑器则不一定会存在&#xff1b; 很多个别软件的编辑接口都会主动呼叫 vi (例如未来会谈到的 crontab, visudo, edquota 等指令)&#x…

【笔记】Day2.4表设计说明

主键ID一般使用bigint类型 运送类型 使用比int更小的tinyint类型 eg&#xff1a;普快代表1 特快代表2&#xff08;没写反&#xff09; 关联城市 varchar 2代表京津冀 3代表江浙沪 4代表川渝 首重和续重都有小数点 故使用double 轻抛系数都为整数 故使用int 创建时间和修改…

【UI】将 naive ui 的 message 封装进axios 中,关于naiveui的message相关的用法

文章目录 前言在setup外进行使用直接包裹使用vue 单文件中使用 参考文章&#xff1a; 关于naiveui的message相关的用法 前言 最近新建了一个vite vu3 的项目&#xff0c;完全是从0 到1 &#xff0c;封装到request 的时候 想对axios 请求做一个全局的处理&#xff0c;但发现…

jmeter学习(7)beanshell

beanshell preprocessor 发送请求前执行 beanshell postprocessor 发送请求前执行 获取请求相关信息 String body sampler.getArguments().getArgument(0).getValue(); String url sampler.getPath(); 获取响应报文 String responseprev.getResponseDataAsString(); 获…

实战OpenCV之视频处理

基础入门 视频是由一系列连续的图像帧组成的&#xff0c;这些帧按照一定的速率连续播放&#xff0c;从而形成动态画面。与视频相关的主要参数有&#xff1a;分辨率、帧率、码率、编解码器、帧类型、文件格式等&#xff0c;下面分别进行介绍。 1、帧率。表示每秒显示的图像帧数&…

【devops】x-ui 实现一键安装 x-ray 打造高速国际冲浪 | xray管理平台

一、部署X-UI篇 1、Github 地址&说明 github地址如下&#xff1a; https://github.com/FranzKafkaYu/x-ui?tabreadme-ov-file 2、一键部署 2.1、更新并安装curl #Ubuntu、Deibian系统 apt update && apt upgrade -y apt install curl -y #CentOS7 系统 yum…

强大的JVM监控工具

介绍 在生产环境中&#xff0c;经常会遇到各种各样奇葩的性能问题&#xff0c;所以掌握最基本的JVM命令行监控工具还是很有必要的 名称主要作用jps查看正在运行的Java进程jstack打印线程快照jmap导出堆内存映像文件jstat查看jvm统计信息jinfo实时查看和修改jvm配置参数jhat用…

现代身份和访问管理 IAM 如何降低风险

您的公司是否仍在使用 1998 年时的身份管理系统&#xff1f;仅凭用户名和密码就能登录本地网络并访问几乎所有资源吗&#xff1f; 虽然大多数企业已经转向现代身份和访问管理(IAM) 平台&#xff0c;但成千上万的企业和其他组织仍然依赖过时的用户名/密码系统。 如果你看一下传…

SpringBoot 整合 阿里云 OSS图片上传

一、OOS 简介 ‌阿里云OSS&#xff08;Object Storage Service&#xff09;是一种基于云存储的产品&#xff0c;适用于存储和管理各种类型的文件&#xff0c;包括图片、视频、文档等。‌ 阿里云OSS具有高可靠性、高可用性和低成本等优点&#xff0c;因此被广泛应用于各种场景&…

简单的网络爬虫爬取视频

示例代码爬取一个周杰伦相关视频 import requests# 自己想下载的视频链接 video_url https://vdept3.bdstatic.com/mda-qg8cnf4bw5x6bjs5/cae_h264/1720516251158906693/mda-qg8cnf4bw5x6bjs5.mp4?v_from_shkapp-haokan-hbf&auth_key1728497433-0-0-4a32e13f751e04754e4…

oracle-函数-instr()的妙用以及相似功能like

INSTR(C1,C2[,I[,J]]) 【功能】在一个字符串中搜索指定的字符,返回发现指定的字符的位置; 【说明】多字节符(汉字、全角符等)&#xff0c;按1个字符计算 【参数】 C1 被搜索的字符串 C2 希望搜索的字符串 I 搜索的开始位置,默认为1 J 第J次出现的位置,默认为1 【…

算法修炼之路之位运算

目录 一:位运算符及一些常用结论总结 1.给一个数n&#xff0c;确定它的二进制表示中的第x位是0还是1(位数从右向左0开始增加) 2.将一个数n的二进制表示形式的第x位修改成1 3.将一个数n的二进制表示的第x位修改为0 4.提取一个数n的二进制表示中最右侧的1 5.干掉一个数n的…

单片机闪存,闪存缓冲取,闪存延迟

一、启用闪存预取缓冲区&#xff08;FLASH_PrefetchBufferCmd (FLASH_PrefetchBuffer_Enable);&#xff09; 闪存预取缓冲区的作用&#xff1a; 在微控制器中&#xff0c;闪存是用于存储程序代码和常量数据的非易失性存储器。当微控制器执行程序时&#xff0c;需要从闪存中读取…

kubernetes-强制删除命名空间

一、故障现象 1、删除命名空间卡住、强制删除也卡住 2、其他终端显示命名空间下无资源 二、处理步骤 1、kubectl get namespace cilium-test -o json > temp.json 获取你需要删除的命名空间json描述文件。 2、修改finalize字段 3、替换 kubectl replace --r…

OmniH2O——通用灵巧且可全身远程操作并学习的人形机器人(其前身H2O是HumanPlus的重要参考)

前言 由于我司一直在针对各个工厂、公司、客户特定的业务场景&#xff0c;做解决方案或定制开发&#xff0c;所以针对每一个场景&#xff0c;我们都会反复考虑用什么样的机器人做定制开发 于此&#xff0c;便不可避免的追踪国内外最前沿的机器人技术进展&#xff0c;本来准备…

信息安全工程师(42)VPN类型和实现技术

前言 VPN&#xff08;Virtual Private Network&#xff0c;虚拟专用网络&#xff09;是一种在公共网络上建立专用网络连接的技术。 一、VPN类型 VPN可以根据不同的分类标准划分为多种类型&#xff0c;主要包括以下几种&#xff1a; 按协议分类&#xff1a; PPTP&#xff08;Poi…

创建osd加入集群

故障原因&#xff1a;ceph节点一个磁盘损坏&#xff0c;其中osd69 down了&#xff0c;需要更换磁盘并重新创建osd加入ceph集群。 信息采集&#xff1a; 更换磁盘前&#xff0c;查询osd69对应的盘符&#xff1a; 将对应的故障磁盘更换后&#xff0c;并重做raid&#xff0c;然后查…

超轻巧modbus调试助手使用说明

一、使用说明 1.1 数据格式 和其他的modbus采集工具一样&#xff0c;本组件也支持各种数据格式&#xff0c;其实就是高字节低字节的顺序。一般是2字节表示一个数据&#xff0c;后面又有4字节表示一个数据&#xff0c;目前好像还有8字节表示一个数据的设备。不同厂家的设备对应…

C++ | Leetcode C++题解之第457题环形数组是否存在循环

题目&#xff1a; 题解&#xff1a; class Solution { public:bool circularArrayLoop(vector<int>& nums) {int n nums.size();auto next [&](int cur) {return ((cur nums[cur]) % n n) % n; // 保证返回值在 [0,n) 中};for (int i 0; i < n; i) {if …