当前位置: 首页 > news >正文

C++ 并发编程中的锁:总结与实践

C++ 并发编程中的锁:总结与实践

在 C++ 并发编程中,我们使用锁来保护共享数据,防止数据竞争。你提到的 std::mutexstd::lock_guard 是最基础也是最重要的同步原语。但 C++ 标准库提供了更丰富的锁类型来满足不同的并发场景。

1. 互斥锁 (Mutex)

这些锁用于在临界区内提供互斥访问,一次只允许一个线程进入。

std::mutex:最简单、最基础的互斥锁。它不能被同一个线程重复锁定,否则会导致死锁。为了避免忘记解锁,我们总是搭配 RAII 类 std::lock_guardstd::unique_lock 使用。

std::lock_guard 就像一个房间的钥匙,当一个线程拿到钥匙(构造时加锁)后,其他所有想要进入的线程都必须等待,直到它离开房间(离开作用域时自动解锁)。

// 示例:使用 std::mutex 和 std::lock_guard
#include <iostream>
#include <thread>
#include <mutex>int shared_counter = 0;
std::mutex mtx;void increment_counter() {for (int i = 0; i < 10000; ++i) {std::lock_guard<std::mutex> lock(mtx); // RAII 加锁shared_counter++;} // 离开作用域时自动解锁
}int main() {std::thread t1(increment_counter);std::thread t2(increment_counter);t1.join();t2.join();std::cout << "Final counter value: " << shared_counter << std::endl;return 0;
}

2. 灵活的锁 (Unique Lock)

在某些复杂场景中,lock_guard 的功能可能不够用。std::unique_lock 就像一个可以定制功能的智能钥匙。它提供了延迟加锁、可移动所有权、以及在临界区结束前提前解锁等功能。

以下代码展示了 std::unique_lock 的延迟加锁功能,这在需要同时锁定多个互斥锁以避免死锁时非常有用。

// 示例:使用 std::unique_lock
#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx1;
std::mutex mtx2;void complicated_task() {std::unique_lock<std::mutex> lock1(mtx1, std::defer_lock); // 延迟加锁std::unique_lock<std::mutex> lock2(mtx2, std::defer_lock); // 延迟加锁// 使用 std::lock 确保同时加锁,避免死锁std::lock(lock1, lock2);// ... 在此执行临界区任务 ...std::cout << "Locks acquired for complicated task." << std::endl;
}int main() {std::thread t1(complicated_task);std::thread t2(complicated_task);t1.join();t2.join();return 0;
}

std::lock 如何解决死锁?
std::lock 函数被设计用来原子地(atomically)同时锁定多个互斥量。

  • 工作原理std::lock 会尝试同时获取所有传递给它的互斥锁。如果它成功地获取了所有锁,就会返回。如果它只获取了部分锁,但无法获取剩余的锁,它会自动释放所有已经获取的锁,然后再次尝试,直到所有锁都被成功获取。
  • 避免死锁:这种“要么全部获取,要么一个都不获取”的策略,从根本上消除了因获取顺序不同而导致的死锁。无论有多少线程同时调用 std::lock,它们都会以一种无死锁的算法来争夺这些锁。

3. 读写分离 (Shared Mutex)

想象一下图书馆,允许多人同时进来读书(读操作),但当有人想要整理书架(写操作)时,必须要求所有人离开。

std::shared_mutex 就是这样的锁。它有两把钥匙:一把是独占锁,由 std::unique_lock 管理,只允许一个线程进入写;另一把是共享锁,由 std::shared_lock 管理,允许多个线程同时进入读。这在读操作远多于写操作的场景中,可以极大地提升程序的并发性能。

// 示例:使用 std::shared_mutex
#include <iostream>
#include <unordered_map>
#include <string>
#include <thread>
#include <shared_mutex>
#include <chrono>class ThreadSafeCache {
private:std::unordered_map<int, std::string> cache_;mutable std::shared_mutex mutex_; // 读写锁public:// 读操作:使用 std::shared_lock 获取共享锁void get(int key) const {std::shared_lock<std::shared_mutex> lock(mutex_);if (cache_.count(key)) {std::cout << "Reader thread: Found key " << key << std::endl;}}// 写操作:使用 std::unique_lock 获取独占锁void set(int key, const std::string& value) {std::unique_lock<std::shared_mutex> lock(mutex_);cache_[key] = value;std::cout << "Writer thread: Wrote key " << key << std::endl;}
};void reader_task(ThreadSafeCache& cache) {for (int i = 0; i < 5; ++i) {cache.get(i);std::this_thread::sleep_for(std::chrono::milliseconds(5));}
}void writer_task(ThreadSafeCache& cache) {for (int i = 0; i < 5; ++i) {cache.set(i, "data");std::this_thread::sleep_for(std::chrono::milliseconds(20));}
}int main() {ThreadSafeCache cache;std::thread t1(writer_task, std::ref(cache));std::thread t2(reader_task, std::ref(cache));std::thread t3(reader_task, std::ref(cache));t1.join();t2.join();t3.join();return 0;
}// --- 程序输出(示例,每次运行结果可能不同)---
// Reader 0 read: Value-0
// Reader 2 read: Value-0
// Reader 1 read: Value-0
// Writer wrote: 0 -> Value-0
// Reader 3 read: Value-0
// Writer wrote: 1 -> Value-1
// Reader 0 read: Value-1
// Reader 2 read: Value-1
// Writer wrote: 2 -> Value-2
// Reader 1 read: Value-2
// Reader 3 read: Value-2
// Reader 0 read: Value-2
// Writer wrote: 3 -> Value-3
// Reader 2 read: Value-3
// Reader 1 read: Value-3
// Reader 3 read: Value-3
// Writer wrote: 4 -> Value-4
// Reader 0 read: Value-4
// Reader 2 read: Value-4
// Reader 1 read: Value-4
// Reader 3 read: Value-4
http://www.dtcms.com/a/352434.html

相关文章:

  • 绝命毒师模拟器2|单机+联机+绝命毒师模拟器1 全DLC(Drug Dealer Simulator 2+1)免安装中文版
  • 事件驱动架构详解
  • AI Agent安全的“阿喀琉斯之踵”:深度解析MCP核心风险与纵深防御架构
  • Python爬虫: 分布式爬虫架构讲解及实现
  • mysql是怎样运行的(梳理)
  • Java基础第二课:hello word
  • 传统联邦 VS 联邦+大模型
  • freeModbus TCP收发数据一段时间后,出现掉线情况(time out问题)
  • 依托边缘计算方案,移动云全面化解算力、效率、安全平衡难题
  • Wireshark捕获数据的四种层次
  • 【Python数据分析】商品数据可视化大屏项目
  • YggJS RButton 按钮组件 v1.0.0 使用教程
  • 亚马逊运营效能提升:广告策略优化与自配送售后管理的协同路径
  • Makefile构建优化:提升编译效率的关键
  • 打卡day49
  • RocketMq程序动态创建Topic
  • 在 Ubuntu 下遇到 <string>头文件找不到的问题
  • 运筹优化(OR)-在机器学习(ML)浪潮中何去何从?
  • 独孤思维:无限分发,无成本赚钱的副业
  • JVM分层编译深度解析:完整机制与实践指南
  • 面向世界模型构建的跨模态认知网络工程
  • the scientist and engineer‘s guide to DSP:1 The Breadth and Depth of DSP 引言
  • CSS实现内凹圆角边框技巧(高频)
  • 【C++】用哈希表封装unordered_XX
  • 西游记24-26:万寿山,五庄观,镇元子;猴子偷果,猪八戒吃人参果——食而不知其味;逃跑被抓回,替师傅受罚;到处求仙,最终观音菩萨救树
  • Qt数据结构与编码技巧全解析
  • LeetCode 2140. 解决智力问题
  • 力扣(滑动窗口最大值)
  • LeetCode 刷题【53. 最大子数组和】
  • 一篇文章拆解Java主流垃圾回收器及其调优方法。