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.xdnf.cn/news/1559433.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【数据结构与算法】简单聊聊图数据的存储

文章目录 1. 邻接矩阵&#xff08;Adjacency Matrix&#xff09;2. 邻接表&#xff08;Adjacency List&#xff09;3. 邻接多重表4. 十字链表5. 图数据库&#xff08;Graph Database&#xff09; 存储图数据的方法主要有几种&#xff0c;每种方法都有其特定的应用场景和优缺点。…

毕业设计项目-古典舞在线交流平台的设计与实现(源码/论文)

项目简介 基于springboot实现的&#xff0c;主要功能如下&#xff1a; 技术栈 后端框框&#xff1a;springboot/mybatis 前端框架&#xff1a;html/JavaScript/Css/vue/elementui 运行环境&#xff1a;JDK1.8/MySQL5.7/idea&#xff08;可选&#xff09;/Maven3&#xff08…

什么是物联网nb水表?

物联网NB水表是一种利用NB-IoT(窄带物联网)技术实现远程数据传输的智能水表。这种水表不仅能够精确计量用户的用水量&#xff0c;还能通过无线通信技术实现数据的远程传输和管理。下面我们来详细介绍物联网NB水表的主要特点和功能。 一、基本概念 -定义&#xff1a;物联网NB水…

如何优化spotbugsXml.xml文件来方便debug的落地方案来了

不优化的spotbugsXml.xml 使用maven 构建来运行spotbugs的小伙伴都知道&#xff0c;执行完下面的命令后 mvn clean install -U spotbugs:spotbugs 会在默认的在target目录下生成一个spotbugsXml.xml 文件&#xff0c;而打开这个文件&#xff0c;想要debug里面的具体问题&am…

嵌入式面试——FreeRTOS篇(六) 任务通知

本篇为&#xff1a;FreeRTOS 任务通知篇 任务通知简介 1、任务通知介绍 答&#xff1a; 任务通知&#xff1a;用来通知任务的&#xff0c;任务控制块中的结构体成员变量ulNotifiedValue就是这个通知值。 使用队列、信号量、事件标志组时都需要另外创建一个结构体&#xff0c…

Ubuntu终端配置

选择shell shell有很多&#xff0c;默认的是bash&#xff0c;一般就够用里&#xff0c;想要花里胡哨点就用zsh&#xff0c;还有最近比较火的fish 如果在刚开始安装完Ubuntu没有改shell&#xff0c;后面就不要改了。 安装的软件会设置环境变量&#xff0c;这些环境变量都是写入…

QDateTime 使用详解

QDateTime 是 Qt 框架中用于处理日期和时间的类。本篇文章详细介绍、通过示例 快速了解QDateTime的各种操作&#xff0c;包括: 当前时间、获取日期和时间、获取日期、获取时间、获取时间戳、格式化输出、年、月、日、QTime时间、获取微妙、操作日期和时间、添加时间、减去时间、…

无人机避障——4D毫米波雷达点云滤波去噪(四)

噪声的来源&#xff1a; 对于4D毫米波雷达的前后两帧点云数据进行去噪&#xff0c;可以采用多种方法。首先&#xff0c;需要了解点云数据的噪声来源&#xff0c;可能是由于硬件限制、环境干扰或目标本身的反射特性等因素造成的。噪声点通常包括漂移点、孤立点、冗余点和混杂点…

毕业设计项目——基于RISC-V的标签化跨层调度应用任务管理(论文/代码)

完整的论文代码见文章末尾 以下为核心内容 摘要 在现代操作系统中&#xff0c;高效的系统调度策略对于优化系统性能、提高资源利用率和保证系统稳定性至关重要。本文提出了一个基于Linux进程文件系统&#xff08;procfs&#xff09;的系统监控工具&#xff0c;旨在通过实时收…

Spring Cloud全解析:链路追踪之springCloudSleuth简介

文章目录 springCloudSleuth简介链路追踪&#xff1f;SpringCloudSleuth术语链路示意图zipkin依赖配置 springCloudSleuth简介 链路追踪&#xff1f; 什么是链路追踪&#xff1f;就是将一次分布式请求还原成调用链路&#xff0c;将一次分布式请求的调用情况集中展示&#xff…

算法:1、动态规划算法DP(Dynamic Programming)

算法介绍 动态规划&#xff08;Dynamic Programming&#xff0c;DP&#xff09;‌&#xff0c;通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。它的关键思想是对于最终结果依赖前序步骤的问题&#xff0c;将结果定义为状态值dp&#xff0c;然后推导出后续步骤由…

深度学习常见问题

1.YOLOV5和YOLOV8的区别 YOLOv5 和 YOLOv8 是两个版本的 YOLO&#xff08;You Only Look Once&#xff09;目标检测算法&#xff0c;它们在网络架构、性能优化、功能扩展等方面有显著的区别。YOLOv5 是 YOLO 系列的重要改进版本&#xff0c;而 YOLOv8 是最新的一次重大升级&am…

SQL性能优化指南:如何优化MySQL多表join场景

目录 多表join问题SQL 这里解释下 Using join buffer (Block Nested Loop)&#xff1a; 对性能产生的影响&#xff1a; 三种join算法介绍 join操作主要使用以下几种算法&#xff1a; &#xff08;1&#xff09;Nested Loop Join &#xff08;2&#xff09;Block Nested …

搭建企业域名服务器案例

任务要求&#xff1a; 某企业要建立一台应用于以下情况的主域名服务器 拥有一个C类网段地址&#xff0c;为202.101.55.0。企业域名注册为company.com。域名服务器的IP地址定位为202.101.55.55&#xff0c;主机名为dns.company.com。企业网通过路由器与Internet连接。要解析的…

第九届清洁能源与发电技术国际学术会议(CEPGT 2024)

第九届清洁能源与发电技术国际学术会议&#xff08;CEPGT 2024&#xff09; 2024 9th International Conference on Clean Energy and Power Generation Technology (CEPGT 2024) 【早投稿早录用&#xff0c;享受早鸟优惠】 第九届清洁能源与发电技术国际学术会议&#xff0…

记录一个Ajax发送JSON数据的坑,后端RequestBody接收参数小细节?JSON对象和JSON字符串的区别?

上半部分主要介绍我实际出现的问题&#xff0c;最终下面会有总结。 起因&#xff1a;我想发送post请求的data&#xff0c;但是在浏览器中竟然被搞成了地址栏编码 如图前端发送的ajax请求数据 如图发送的请求体&#xff1a; 很明显是keyvalue这种形式&#xff0c;根本就不是…

开源的键鼠共享工具「GitHub 热点速览」

十一长假回来&#xff0c;我的手放在落灰的键盘上都有些陌生了&#xff0c;红轴竟敲出了青轴般的响声&#xff0c;仿佛在诉说对假期结束的不甘。 假期回归的首更&#xff0c;让我们看看又有什么好玩的开源项目冲上了开源热榜。一套键盘和鼠标控制多台电脑的工具 deskflow&#…

supOS加速数实融合发展

作为工业操作系统领军企业&#xff0c;蓝卓受邀参加2024金砖国家新工业革命伙伴关系论坛&#xff0c;深度参与多个环节。在9月11日召开的金砖国家新工业革命伙伴关系论坛产融合作专题研讨上&#xff0c;蓝卓总经理谭彰分享了supOS在产融协同的最新实践&#xff0c;以及supOS进入…

云上考场小程序+ssm论文源码调试讲解

2 关键技术简介 2.1 微信小程序 微信小程序&#xff0c;简称小程序&#xff0c;英文名Mini Program&#xff0c;是一种全新的连接用户与服务的方式&#xff0c;可以快速访问、快速传播&#xff0c;并具有良好的使用体验。 小程序的主要开发语言是JavaScript&#xff0c;它与…

集师知识付费小程序:打造培训机构在线教育的金字招牌 集师知识付费系统 集师知识付费小程序 集师知识服务系统 集师线上培训系统 集师线上卖课小程序

在数字化浪潮的推动下&#xff0c;在线教育已成为教育领域的热门话题。而在众多在线教育平台中&#xff0c;集师知识付费小程序凭借其独特的定位和创新的模式&#xff0c;成功为培训机构打造了一张闪亮的在线教育金字招牌。 集师知识付费小程序&#xff0c;是一个集课程展示、…