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

C++ 多线程同步机制详解

1. 互斥量 (Mutex)

什么是互斥量?

互斥量是最基本的同步原语,用于保护共享数据,防止多个线程同时访问。

std::mutex

cpp

#include <mutex>
#include <thread>
#include <iostream>std::mutex mtx;
int shared_data = 0;void increment() {for (int i = 0; i < 100000; ++i) {mtx.lock();        // 获取锁++shared_data;     // 临界区mtx.unlock();      // 释放锁}
}int main() {std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();std::cout << "Final value: " << shared_data << std::endl;  // 总是 200000return 0;
}

不同类型的互斥量

cpp

#include <mutex>
#include <shared_mutex>// 1. 基本互斥量
std::mutex m1;// 2. 递归互斥量(同一线程可多次锁定)
std::recursive_mutex m2;// 3. 定时互斥量(可尝试锁定一段时间)
std::timed_mutex m3;// 4. 递归定时互斥量
std::recursive_timed_mutex m4;// 5. 共享互斥量(读写锁)
std::shared_mutex m5;

2. 锁管理器 (Lock Guards)

为什么需要锁管理器?

手动管理锁容易忘记解锁,导致死锁。锁管理器基于 RAII 原则,自动管理锁的生命周期。

std::lock_guard

最简单的锁管理器,构造时加锁,析构时自动解锁。

cpp

void safe_increment() {for (int i = 0; i < 100000; ++i) {std::lock_guard<std::mutex> lock(mtx);  // 构造时加锁++shared_data;                          // 临界区} // 析构时自动解锁
}

std::unique_lock

更灵活的锁管理器,支持延迟锁定、手动解锁等。

cpp

void flexible_increment() {std::unique_lock<std::mutex> lock(mtx, std::defer_lock);  // 延迟锁定for (int i = 0; i < 100000; ++i) {lock.lock();        // 手动加锁++shared_data;lock.unlock();      // 手动解锁// 这里可以执行非临界区代码std::this_thread::sleep_for(std::chrono::microseconds(1));lock.lock();        // 重新加锁// 更多临界区操作}
}

unique_lock 的特殊功能

cpp

std::timed_mutex tmtx;void timed_operation() {std::unique_lock<std::timed_mutex> lock(tmtx, std::defer_lock);// 尝试锁定最多100毫秒if (lock.try_lock_for(std::chrono::milliseconds(100))) {// 成功获取锁std::cout << "Lock acquired!" << std::endl;} else {// 超时,未能获取锁std::cout << "Failed to acquire lock within timeout" << std::endl;}// 支持移动语义std::unique_lock<std::timed_mutex> another_lock = std::move(lock);
}

3. 条件变量 (Condition Variables)

什么是条件变量?

允许线程等待特定条件成立,避免忙等待。

std::condition_variable

cpp

#include <condition_variable>
#include <queue>std::mutex mtx;
std::condition_variable cv;
std::queue<int> data_queue;
bool finished = false;// 生产者
void producer() {for (int i = 0; i < 10; ++i) {std::this_thread::sleep_for(std::chrono::milliseconds(100));std::lock_guard<std::mutex> lock(mtx);data_queue.push(i);std::cout << "Produced: " << i << std::endl;cv.notify_one();  // 通知一个等待的消费者}{std::lock_guard<std::mutex> lock(mtx);finished = true;}cv.notify_all();  // 通知所有消费者
}// 消费者
void consumer(int id) {while (true) {std::unique_lock<std::mutex> lock(mtx);// 等待条件:队列不为空或生产结束cv.wait(lock, [] { return !data_queue.empty() || finished; });// 检查是否应该退出if (finished && data_queue.empty()) {break;}// 处理数据if (!data_queue.empty()) {int data = data_queue.front();data_queue.pop();lock.unlock();  // 尽早释放锁std::cout << "Consumer " << id << " consumed: " << data << std::endl;}}
}

条件变量的等待方式

cpp

// 方式1:使用谓词(推荐)
cv.wait(lock, [] { return condition; });// 方式2:无谓词(需要循环检查)
while (!condition) {cv.wait(lock);
}// 带超时的等待
if (cv.wait_for(lock, std::chrono::seconds(1), [] { return condition; })) {// 条件在超时前满足
} else {// 超时
}

4. 原子变量 (Atomic Variables)

什么是原子变量?

提供无需锁的线程安全操作,适合简单的计数器、标志位等。

std::atomic

cpp

#include <atomic>
#include <vector>
#include <thread>std::atomic<int> atomic_counter(0);
std::atomic<bool> ready_flag(false);
std::atomic_flag spinlock = ATOMIC_FLAG_INIT;void atomic_worker() {for (int i = 0; i < 10000; ++i) {// 多种原子操作方式// 1. 使用成员函数atomic_counter.fetch_add(1, std::memory_order_relaxed);// 2. 使用运算符重载// atomic_counter++;// 3. 使用 load/store// int old = atomic_counter.load();// atomic_counter.store(old + 1);  // 这不是原子的!}
}void spinlock_example() {// 获取自旋锁while (spinlock.test_and_set(std::memory_order_acquire)) {// 忙等待}// 临界区// ...// 释放自旋锁spinlock.clear(std::memory_order_release);
}

内存序 (Memory Order)

cpp

std::atomic<int> x(0), y(0);
int r1, r2;void thread1() {x.store(1, std::memory_order_relaxed);  // 宽松顺序y.store(1, std::memory_order_release);  // 释放操作
}void thread2() {r1 = y.load(std::memory_order_acquire); // 获取操作r2 = x.load(std::memory_order_relaxed);
}// 内存序类型:
// - memory_order_relaxed: 只保证原子性
// - memory_order_consume: 数据依赖顺序
// - memory_order_acquire: 获取操作
// - memory_order_release: 释放操作  
// - memory_order_acq_rel: 获取-释放
// - memory_order_seq_cst: 顺序一致性(默认)

5. 综合示例:线程安全队列

cpp

#include <queue>
#include <mutex>
#include <condition_variable>
#include <optional>template<typename T>
class ThreadSafeQueue {
private:mutable std::mutex mtx;std::queue<T> queue;std::condition_variable cv;public:ThreadSafeQueue() = default;// 禁止拷贝ThreadSafeQueue(const ThreadSafeQueue&) = delete;ThreadSafeQueue& operator=(const ThreadSafeQueue&) = delete;void push(T value) {std::lock_guard<std::mutex> lock(mtx);queue.push(std::move(value));cv.notify_one();}std::optional<T> pop() {std::unique_lock<std::mutex> lock(mtx);if (queue.empty()) {return std::nullopt;}T value = std::move(queue.front());queue.pop();return value;}T wait_and_pop() {std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, [this] { return !queue.empty(); });T value = std::move(queue.front());queue.pop();return value;}bool empty() const {std::lock_guard<std::mutex> lock(mtx);return queue.empty();}size_t size() const {std::lock_guard<std::mutex> lock(mtx);return queue.size();}
};

6. 死锁预防

使用 std::lock 同时锁定多个互斥量

cpp

std::mutex mtx1, mtx2;void safe_operation() {// 同时锁定两个互斥量,避免死锁std::lock(mtx1, mtx2);// 使用 lock_guard 管理锁(adopt_lock 表示已锁定)std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);// 临界区操作
}

使用 std::scoped_lock (C++17)

cpp

void safer_operation() {std::scoped_lock lock(mtx1, mtx2);  // 自动同时锁定多个互斥量// 临界区操作
} // 自动解锁所有互斥量

7. 最佳实践总结

  1. 优先使用锁管理器而不是手动锁操作

  2. 保持临界区最小化 - 尽快释放锁

  3. 避免嵌套锁或使用 std::lock 预防死锁

  4. 条件变量总是与互斥量配合使用

  5. 简单操作用原子变量,复杂操作用互斥量

  6. 注意虚假唤醒 - 条件变量等待总是使用谓词

cpp

// 好:使用谓词避免虚假唤醒
cv.wait(lock, [] { return condition; });// 不好:可能虚假唤醒
while (!condition) {cv.wait(lock);
}

这些工具构成了 C++ 多线程编程的基础,正确使用它们可以编写出高效且线程安全的并发程序。

http://www.dtcms.com/a/560963.html

相关文章:

  • EMB电子机械制动器夹紧力分析
  • 计算机操作系统:缓冲区管理
  • 绥化市建设工程网站招投标网站 服务器 域名
  • Altium23批量将元器件的摆放角度恢复正常
  • 陇西网站建设 室内设计网站有pc站和手机站
  • 因果推理算法及工具应用
  • 安卓接入Twitter三方登录
  • CICD工具,Jenkins or Tekton or Arbess一文全面对比评测
  • 高水平的徐州网站建设做好网站内能另外做链接吗
  • 图神经网络分享系列-GAT(GRAPH ATTENTION NETWORKS) (三)
  • 四川手机网站建设费用监理工程师成绩在建设部哪个网站查
  • PyTorch2 Python深度学习 - 全连接神经网络(FNN)
  • Langfuse开源LLM工程平台完整部署实战指南
  • 美工需要会哪些软件前端网站优化
  • 页面白屏如何排查?
  • ESP32 分区表配置指南(ArduinoIDE2.X.X)
  • 如何建一个个人的网站简单网站建设策划书范文
  • 2.基础--MySQL安装及启动
  • 洛阳网站建设汉狮怎么样看动漫是怎么做视频网站
  • 吴恩达新课程:Agentic AI(笔记5)
  • Spring AI--MCP协议
  • 多模态输入框架详解:OpenHarmony Input Kit核心技术与实践
  • 【AI-agent】AI Agent核心概念理解
  • 参与免疫排斥反应的MHC基因位点
  • Broadcast (攻防世界)
  • 【Linux学习】启用NFS服务并挂载
  • Python招聘数据分析可视化系统 Boss直聘数据 selenium爬虫 Flask框架 数据清洗(附源码)✅
  • 上海网站 建设wordpress的域名绑定
  • kotlin - 显示HDR图(heic格式),使用GainMap算法,速度从5秒提升到0.6秒
  • 查找及其算法