C++一分钟之-互斥锁与条件变量
创作时间:
作者:
@小白创作中心
C++一分钟之-互斥锁与条件变量
引用
1
来源
1.
https://developer.aliyun.com/article/1548333
在C++并发编程中,互斥锁和条件变量是保证数据一致性和线程安全的重要工具。本文将深入浅出地讲解这两者的使用方法、常见问题及解决方案,并通过实例代码加深理解。
在C++并发编程中,同步机制是保证数据一致性与线程安全的重要工具。std::mutex
(互斥锁)提供了基本的互斥访问保护,而std::condition_variable
(条件变量)则用于线程间的精确协调,让线程在满足特定条件时才继续执行。本文将深入浅出地讲解这两者的使用、常见问题、易错点以及如何避免这些问题,并通过实例代码加深理解。
一、互斥锁(std::mutex)
互斥锁是实现线程间资源独占访问的基础手段。一旦一个线程获得了锁,其他试图获取同一锁的线程将会被阻塞,直到锁被释放。
基本用法
std::mutex mtx;
// 加锁
mtx.lock();
// 执行临界区代码
// ...
// 解锁
mtx.unlock();
易错点与避免策略
- 忘记解锁:使用
std::lock_guard
或std::unique_lock
自动管理锁的生命周期,确保即使发生异常也能解锁。 - 死锁:避免在持有锁的情况下调用可能阻塞的函数,或按相同的顺序获取多个锁。
二、条件变量(std::condition_variable)
条件变量用于线程间同步,允许一个线程等待(挂起)直到另一个线程通知某个条件为真。
基本用法
std::condition_variable cv;
std::mutex mtx;
void waitingFunction() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{
return conditionToWaitFor;
}); // 条件满足前挂起
// 条件满足后执行的代码
}
void notifyingFunction() {
// 修改状态使得conditionToWaitFor为真
std::lock_guard<std::mutex> lock(mtx);
cv.notify_one(); // 唤醒一个等待的线程
}
常见问题与避免策略
- 无条件唤醒:不要在没有改变条件的情况下调用
notify_*
函数,这可能导致不必要的线程唤醒和重新检查条件。 - 虚假唤醒:即使没有调用
notify_*
,等待的线程也可能被唤醒。因此,总是使用条件来检查是否真正满足继续执行的条件。 - 死锁:确保在调用
wait
之前已经获得了锁,并且在wait
之后立即检查条件,避免在持有锁的情况下执行耗时操作。
三、综合示例:生产者-消费者模型
#include <iostream>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
std::queue<int> producedItems;
std::mutex mtx;
std::condition_variable condVar;
bool doneProducing = false;
void producer(int n) {
for (int i = 0; i < n; ++i) {
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟生产时间
std::lock_guard<std::mutex> lock(mtx);
producedItems.push(i);
condVar.notify_one(); // 通知消费者
if (i == n - 1) doneProducing = true;
}
}
void consumer() {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
condVar.wait(lock, []{
return !producedItems.empty() || doneProducing;
});
if (!producedItems.empty()) {
int item = producedItems.front();
producedItems.pop();
std::cout << "Consumed: " << item << std::endl;
} else if (doneProducing) {
break;
}
}
}
int main() {
std::thread producerThread(producer, 10);
std::thread consumerThread(consumer);
producerThread.join();
consumerThread.join();
return 0;
}
四、总结
互斥锁和条件变量是构建复杂并发系统不可或缺的组件。正确使用它们,可以有效解决线程间的同步问题,避免数据竞争和死锁。实践中,应注重细节,如使用RAII模式管理锁的生命周期、仔细设计条件判断逻辑,以及避免无意义的线程唤醒。通过上述示例和策略的学习,希望你能更加自信地在C++项目中应用这些并发工具,提升程序的并发性能和可靠性。随着经验的积累,逐步探索更高级的并发模式和库,如C++20中的std::latch
和std::barrier
,将使你的并发编程技能更加全面和高效。
本文原文来自阿里云开发者社区
热门推荐
肝癌患者如何进行康复运动?医生给出专业建议
华中科技大学徐辉碧教授获"生物无机化学卓越成就奖"
提升日常资产管理效率的五大关键策略
魔兽世界怀旧服:简易投掷炸弹II的获取与使用完全攻略
中国残疾赔偿金范围及计算标准
企业简易注销办法:优化市场退出机制的法律框架
膜蛋白GPCR研究的挑战与突破方向
治疗引起味觉改变,食物难以下咽?这些营养品和烹饪技巧能改善
微创植牙技术介绍:更舒适的植牙选择
武汉城市圈的发展潜力与未来趋势分析
新加坡必吃15+道地美食及人气餐厅推荐
深度解析香港公司审计中的银行询证函重要性
房屋租赁注意事项及违约金规定详解
关于儿童肿瘤靶向药物治疗,你应当了解的问题
夏原吉:从寒门学子到明朝五朝元老,他如何成就两大盛世?
转基因作物与食品安全:机遇与挑战并存
吉他音箱选购指南:从类型、功率到品牌全方位解析
2024中国乡村旅游发展白皮书
尼克·胡哲:没有四肢,却活出精彩人生
蘑菇是嘌呤高的食物吗?一文读懂蘑菇的嘌呤含量与食用建议
脑科学告诉你:你是不是总觉得自己不够好?试试这4步改写负面脚本
八字测命的理论缺陷
退休后,老人的这10种行为最让人讨厌,希望你一个也没有
深入解析 CPU、GPU 与 NPU:计算世界的核心驱动力
阎锡山太原兵工厂的4种招牌枪械,曾在抗战中还发挥了不小的作用
治疗骨质疏松常用中成药有哪些
手机虚拟内存小科普
土地纠纷赔偿案例解析:赔钱原因及维权途径
微信小程序开发框架构建与实战指南
曲美他嗪,抗心绞痛药物?还是兴奋剂?