线程同步——互斥和条件变量
文章目录1. 互斥锁1.1 互斥锁1.1.1 普通互斥锁加锁代码示例1.1.2 递归互斥锁1.3 自旋锁代码示例1.4 定时锁1.4.1 普通定时锁1.4.2 递归定时锁2 读写锁使用方法代码示例3. 锁保护3.1 lock_guard代码示例实现原理3.2 唯一锁4. 条件变量和消费者模式3.1 条件变量3.2 生产者消费者模式更新中本文将多线程中的一些加锁部分抽取出来深入学习C11部分有了一些崭新的库C17又支持了共享互斥锁。而C98更偏向于C语言的方式所以将C和C区分开展示案例。C语言加锁总结1. 互斥锁1.1 互斥锁1.1.1 普通互斥锁std::mutex说明基础含义只有一个线程可以占用该锁其他线程想要使用是必须等待该线程释放基本用法std::mutex mut_num;mut_num.lock();mut_num.unlock();使用场景多个线程同时对一个资源进行修改时可能发生意向不到的结果所以需要加锁以确保多个线程不会使用混乱注意事项不可重入同一个线程对同一个互斥锁加锁两次会导致死锁手动解锁使用完毕后必须手动解锁否则会导致死锁加锁代码示例//mutex.cpp#includethread#includemutex#includecstring#includechrono#includeiostreamcharg_buf[10];std::mutex mut_buf;#defineMAX_TH3voidpthread_fun1(){mut_buf.lock();strcpy(g_buf,abcdef);std::this_thread::sleep_for(std::chrono::seconds(1));printf(%s\n,g_buf);mut_buf.unlock();}intmain(){std::thread arr1[MAX_TH];for(inti0;iMAX_TH;i){arr1[i]std::thread(pthread_fun1);}for(inti0;iMAX_TH;i){if(arr1[i].joinable())arr1[i].join();}return0;}输出abcdef abcdef abcdef附CMakeLists.txtcmake_minimum_required(VERSION3.10)project(test)set(CMAKE_CXX_STANDARD11)#set(EXECUTABLE_OUTPUT_PATH)add_executable(mutex.exe mutex.cpp)target_link_libraries(mutex.exe pthread)加锁后必须在任何退出的地方解锁否则输出结果程序挂死abcdef1.1.2 递归互斥锁std::recursive_mutex说明基础含义只有一个线程可以占用该锁其他线程想要使用是必须等待该线程释放基本用法std::recursive_mutex mut_num;mut_num.lock();mut_num.unlock();使用场景一个线程多个函数对一个资源加锁或者在递归函数中多次对同一个资源加锁注意事项可重入同一个线程对递归互斥锁加锁两次可正常加锁手动解锁使用完毕后必须手动解锁否则会导致死锁1.3 自旋锁含义自旋锁是指线程在等待解锁时使线程处于忙等的锁这意味着线程将持续占用CPU知道加锁成功。说明C标准库并没有提供专门的自选锁类型但可以使用std::atomic_flag来实现。使用场景自旋锁适用于锁持有时间非常短且线程不希望在操作系统调度中频繁上下文切换的场景。这通常用在低延迟系统中或者当线程数量不多于CPU数量时确保CPU不会在等待锁时空闲。代码示例#includeiostream#includeatomic#includethread#includevectorclassSpinLock{private:std::atomic_flag lock_flagATOMIC_FLAG_INIT;public:voidlock(){while(lock_flag.test_and_set(std::memory_order_acquire)){// 循环等待直到锁变为可用状态}}voidunlock(){lock_flag.clear(std::memory_order_release);}};SpinLock spinlock;voidwork(intid){spinlock.lock();std::coutThread id entered critical section.std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(100));// 模拟工作std::coutThread id leaving critical section.std::endl;spinlock.unlock();}intmain(){std::vectorstd::threadthreads;for(inti0;i5;i){threads.emplace_back(work,i);}for(autoth:threads){th.join();}return0;}1.4 定时锁1.4.1 普通定时锁std::timed_mutexstd::timed_mutex timed_mtx;voidattempt_lock_for(intid){autonowstd::chrono::steady_clock::now();if(timed_mtx.try_lock_for(std::chrono::seconds(1))){std::coutThread id got the lock.std::endl;std::this_thread::sleep_for(std::chrono::seconds(2));timed_mtx.unlock();}else{std::coutThread id couldnt get the lock.std::endl;}}1.4.2 递归定时锁std::recursive_timed_mutex结合了std::recursive_mutex和std::timed_mutextry_lock_for2 读写锁std::shared_mutex说明头文件#include shared_mutex基础含义允许多个线程读同一个资源但只允许一个资源写入使用场景适用于读操作远多于写操作的情况注意事项可重入同一个线程对递归互斥锁加锁两次可正常加锁手动解锁使用完毕后必须手动解锁否则会导致死锁版本:C17后才可使用使用方法std::shared_mutex mut_num;mut_num.lock();mut_num.unlock();mut_num.lock_shared();mut_num.unlock_shared();mut_num.join();// 主线程阻塞等待子线程执行结束代码示例#includethread#includeshared_mutex#includecstring#includeiostreamcharg_buf[10];std::shared_mutex mtx;#defineMAX_TH3voidpthread_write(){mtx.lock();strcpy(g_buf,abcdef);std::this_thread::sleep_for(std::chrono::seconds(1));printf(write: %s\n,g_buf);mtx.unlock();}voidpthread_read(){mtx.lock_shared();std::this_thread::sleep_for(std::chrono::seconds(1));printf(read: %s\n,g_buf);mtx.unlock_shared();}intmain(){std::thread arr1[MAX_TH],arr2[MAX_TH];for(inti0;iMAX_TH;i){arr1[i]std::thread(pthread_write);arr2[i]std::thread(pthread_read);}for(inti0;iMAX_TH;i){if(arr1[i].joinable()){arr1[i].join();}if(arr2[i].joinable()){arr2[i].join();}}return0;}输出结果读的线程同时输出写的线程隔一秒输出write:abcdef read:abcdef read:abcdef read:abcdef write:abcdef write:abcdef3. 锁保护在加锁时最大的危险不是不会加锁而是在使用完毕后忘记释放锁。本章节将介绍几种更安全的加锁方式。3.1 lock_guardstd::lock_guard可以实现锁的自动管理在作用域结束后自动释放锁。**实现原理**lock_guard类对象为局部变量在函数声明周期结束时对象的声明周期结束自动调用析构函数实现解锁。代码示例#includethread#includemutex#includecstring#includechrono#includeiostreamcharg_buf[10];std::mutex mtx;#defineMAX_TH3voidpthread_fun1(){std::lock_guardstd::mutexlock(mtx);strcpy(g_buf,abcdef);std::this_thread::sleep_for(std::chrono::seconds(1));printf(%s\n,g_buf);}intmain(){std::thread arr1[MAX_TH];for(inti0;iMAX_TH;i){arr1[i]std::thread(pthread_fun1);}for(inti0;iMAX_TH;i){if(arr1[i].joinable())arr1[i].join();}return0;}实现原理templatetypenameTclassself_lock{public:self_lock(Tpara){plockInstpara;plockInst-lock();}~self_lock(){if(plockInst){plockInst-unlock();}}private:T*plockInst;};3.2 唯一锁std::unique_lock也是可以自动解锁但比std::lock_guard的功能更丰富。唯一锁支持第二个参数唯一锁详解4. 条件变量和消费者模式3.1 条件变量条件变量是一种同步原语它可以阻塞一个或多个线程直到某个特定条件为真。条件变量总是与互斥锁一起使用以避免竞争条件。基本操作包括等待线程获取互斥锁成功后若条件变量不满足则释放互斥锁并进入阻塞状态直到另一个线程通知条件变量通知通知其他线程重新获取其他线程会重新获取互斥锁并重新判断条件变量是否满足条件优势避免线程获取锁成功后由于一些条件不满足而一直处于忙等待状态代码示例#includethread#includemutex#includecstring#includechrono#includevector#includeiostream#includecondition_variable#includeatomic#defineMAX_PRODUCT_NUM10std::mutex mtx;std::condition_variable cv;std::vectorintproducts(1,1);std::atomicboolstop_flag(false);// 用于控制程序退出voidfun_producer(){while(!stop_flag){std::unique_lockstd::mutexlock(mtx);cv.wait(lock,[]{returnproducts.size()MAX_PRODUCT_NUM||stop_flag;});if(stop_flag){break;}products.push_back(1);products.push_back(1);products.push_back(1);products.push_back(1);products.push_back(1);std::coutmake 5std::endl;lock.unlock();cv.notify_all();std::this_thread::sleep_for(std::chrono::seconds(1));}}voidfun_consumer(intid){while(!stop_flag){std::unique_lockstd::mutexlock(mtx);cv.wait(lock,[]{returnproducts.size()0||stop_flag;});if(stop_flag){break;}if(products.size()0){products.pop_back();lock.unlock();std::coutidtakestd::endl;cv.notify_one();}else{lock.unlock();std::coutid:id no productstd::endl;cv.notify_all();}std::this_thread::sleep_for(std::chrono::seconds(1));}}intmain(){std::thread producerstd::thread(fun_producer);std::thread consumer1std::thread(fun_consumer,1);std::thread consumer2std::thread(fun_consumer,2);std::this_thread::sleep_for(std::chrono::seconds(10));// 设置停止标志stop_flagtrue;cv.notify_all();producer.join();consumer1.join();consumer2.join();return0;}3.2 生产者消费者模式使用互斥锁实现单生产者多消费者#includethread#includemutex#includecstring#includechrono#includevector#includeiostream#includecondition_variable#defineMAX_PRODUCT_NUM10std::mutex mtx;std::condition_variable cv;std::vectorintproducts(1,1);voidfun_producer(){while(1){std::unique_lockstd::mutexlock(mtx);cv.wait(lock,[]{returnproducts.size()MAX_PRODUCT_NUM;});products.push_back(1);printf(make\n);lock.unlock();cv.notify_all();std::this_thread::sleep_for(std::chrono::seconds(1));}}voidfun_consumer(intid){while(1){std::unique_lockstd::mutexlock(mtx);if(cv.wait_for(lock,std::chrono::seconds(1),[]{returnproducts.size()0;})){products.pop_back();lock.unlock();printf(id(%u)take\n,id);cv.notify_all();}else{lock.unlock();std::coutid:id no productstd::endl;cv.notify_all();}std::this_thread::sleep_for(std::chrono::seconds(1));}}intmain(){std::thread producerstd::thread(fun_producer);std::thread consumer1std::thread(fun_consumer,1);std::thread consumer2std::thread(fun_consumer,2);producer.join();consumer1.join();consumer1.join();return0;}

相关新闻

【保姆级教程】小米路由器刷OpenWRT软路由系统并安装内网穿透配置公网地址

【保姆级教程】小米路由器刷OpenWRT软路由系统并安装内网穿透配置公网地址

文章目录前言1. 安装Python和需要的库2. 使用 OpenWRTInvasion 破解路由器3. 备份当前分区并刷入新的Breed4. 安装cpolar内网穿透4.1 注册账号4.2 下载cpolar客户端4.3 登录cpolar web ui管理界面4.4 创建公网地址5. 固定公网地址访问前言 今天分享一下如何在小米路由器4A千兆…

2026/7/23 18:33:00阅读更多 →
Copilot 到 Agent 的工程断层:我们团队在任务拆解和工具链上踩的 4 个坑

Copilot 到 Agent 的工程断层:我们团队在任务拆解和工具链上踩的 4 个坑

从Copilot到智能开发Agent:工程化落地的7个关键差异与实战经验 去年我们给12人前端团队全员配备Copilot时,以为从此就能坐等生产力飙升。直到三个月前尝试接入能调用API、自主拆解需求的Agent系统,才发现从代码补全到任务自治之间存在工程断…

2026/7/23 18:33:00阅读更多 →
YOLOv5在实时人物情绪识别中的实践与优化

YOLOv5在实时人物情绪识别中的实践与优化

1. 项目概述:YOLOv5在人物情绪识别中的应用YOLOv5作为当前最流行的实时目标检测算法之一,其轻量高效的特性使其在边缘计算场景中表现优异。将YOLOv5应用于人物情绪识别,本质上是通过面部表情检测来实现情绪分类的计算机视觉任务。与传统基于分…

2026/7/23 18:33:00阅读更多 →
Linux僵尸进程全解

Linux僵尸进程全解

1、僵尸进程是什么?僵尸进程是 Linux 进程的一种特殊状态(Z状态)。它指的是一个已经执行完毕(terminated)的子进程,但其退出状态信息仍然保留在系统进程表中,等待其父进程来读取(rea…

2026/7/23 19:45:17阅读更多 →
N2HET高精度定时器:双时钟域架构与中心对称PWM实战

N2HET高精度定时器:双时钟域架构与中心对称PWM实战

1. 深入解析N2HET高精度定时器:从循环分辨率到高分辨率结构在嵌入式系统,尤其是电机控制、数字电源和精密工业自动化领域,对时序精度的要求近乎苛刻。一个PWM信号的微小抖动,一次脉冲捕获的延迟,都可能直接导致系统效率…

2026/7/23 19:45:17阅读更多 →
Windows宿主机下的LEDE软路由避坑指南:多网口单臂路由的VLAN配置详解

Windows宿主机下的LEDE软路由避坑指南:多网口单臂路由的VLAN配置详解

Windows宿主机下的LEDE软路由避坑指南:多网口单臂路由的VLAN配置详解 最近在Windows平台上折腾软路由的朋友越来越多了,尤其是那些手头有闲置小主机或者多网口工控机的玩家。Hyper-V作为Windows自带的虚拟化平台,部署软路由确实方便,但真要把多网口设备做成一个高效的单臂…

2026/7/23 19:45:17阅读更多 →
DecoTV高级功能:网盘搜索与FFmpeg视频处理技巧

DecoTV高级功能:网盘搜索与FFmpeg视频处理技巧

DecoTV高级功能:网盘搜索与FFmpeg视频处理技巧 【免费下载链接】DecoTV 基于最新版LunaTV二次开发的一个开箱即用的、跨平台的影视聚合播放站。【原KatelyaTV】 项目地址: https://gitcode.com/gh_mirrors/de/DecoTV DecoTV作为一款基于最新版LunaTV二次开发…

2026/7/23 19:45:17阅读更多 →
ts3480,g6080,mp288,e518,e608,ip2780,mp496,g3400,g4800支持代码5B00,5B02,5B04,1700,1702,1704,P07,E08佳能清零软件

ts3480,g6080,mp288,e518,e608,ip2780,mp496,g3400,g4800支持代码5B00,5B02,5B04,1700,1702,1704,P07,E08佳能清零软件

蓝奏云:点这里下载 密码:00 百度云:点这里下载 备用:pan.baidu.com/s/1gls2G4rqWWP-Mw-z6tVjnQ?pwd0000 常见型号如下: G1000、G1100、G1200、G1400、G1500、G1800、G1900、G1010、G1110、G1120、G1410、G1420、G1411、G151…

2026/7/23 19:45:17阅读更多 →
深入解析TI C2000 eCAP模块:捕获与APWM双模实战指南

深入解析TI C2000 eCAP模块:捕获与APWM双模实战指南

1. eCAP模块:嵌入式系统里的“时间侦探”与“波形雕刻师”在嵌入式系统,尤其是实时控制领域里,时间就是一切。无论是测量电机编码器脉冲的间隔来推算转速,还是生成一个精准的PWM信号去控制LED亮度或电机扭矩,背后都需要…

2026/7/23 19:43:17阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/23 0:56:31阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/23 0:56:31阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/23 0:56:31阅读更多 →
Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具 【免费下载链接】chitchatter Secure peer-to-peer chat that is serverless, decentralized, and ephemeral 项目地址: https://gitcode.com/gh_mirrors/ch/chitchatter Chitchatter是一款革命性的安…

2026/7/23 0:00:28阅读更多 →
从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

更多请点击: https://intelliparadigm.com 第一章:从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表) 当AI副业主理人不再仅满足于单次服务交付,而是主动构建可复用、可裂变、可…

2026/7/23 0:00:28阅读更多 →
油泥处理设备哪里能买到

油泥处理设备哪里能买到

油泥处理设备哪里有?这是许多从事油田、炼化、清罐业务的从业者最关心的问题。根据河南三丰环保设备有限公司的行业经验,选购油泥处理设备的核心在于设备能否适配当地环保法规与原料特性,而非单纯看价格。该公司总经理王钦田先生指出&#xf…

2026/7/23 0:00:28阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/22 22:56:18阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/23 18:58:18阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/23 18:58:18阅读更多 →