C++怎么使用std::mutex进行线程同步_C++并发编程与互斥锁应用

std::mutex用于保护共享数据,配合std::lock_guard或std::unique_lock实现线程安全;示例展示两个线程对计数器加锁递增,确保结果正确;std::lock_guard自动管理锁生命周期,防止死锁;std::unique_lock支持延迟加锁和手动控制,适用于复杂场景;使用std::lock和固定顺序获取多把锁可避免死锁;合理控制临界区大小以提升性能。

在C++多线程编程中,std::mutex 是最常用的同步工具之一,用于保护共享数据,防止多个线程同时访问造成数据竞争。它定义在 头文件中,配合 std::lock_guardstd::unique_lock 使用,能有效确保临界区的互斥访问。

基本用法:保护共享资源

当多个线程需要修改同一个变量时,必须使用互斥锁来串行化访问。例如,两个线程同时对一个全局计数器进行递增操作,若不加锁,可能导致结果错误。

示例代码:

#include zuojiankuohaophpcniostreamyoujiankuohaophpcn
#include zuojiankuohaophpcnthreadyoujiankuohaophpcn
#include zuojiankuohaophpcnmutexyoujiankuohaophpcn

int counter = 0;
std::mutex mtx;  // 定义互斥锁

void increment() {
    for (int i = 0; i zuojiankuohaophpcn 100000; ++i) {
        std::lock_guardzuojiankuohaophpcnstd::mutexyoujiankuohaophpcn lock(mtx);  // 自动加锁/解锁
        ++counter;
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);

    t1.join();
    t2.join();

    std::cout zuojiankuohaophpcnzuojiankuohaophpcn "Final counter value: " zuojiankuohaophpcnzuojiankuohaophpcn counter zuojiankuohaophpcnzuojiankuohaophpcn std::endl;
    return 0;
}

在这个例子中,std::lock_guard 在构造时自动加锁,析构时自动释放锁,避免了忘记解锁导致的死锁问题。

std::unique_lock 的灵活控制

相比 std::lock_guardstd::unique_lock 提供了更多灵活性,比如可以延迟加锁、手动控制加锁与解锁时机,甚至支持条件变量。

示例:手动控制锁的范围

std::mutex mtx;
std::unique_lockzuojiankuohaophpcnstd::mutexyoujiankuohaophpcn lock(mtx, std::defer_lock); // 不立即加锁

// ... 做一些不需要锁的操作

lock.lock();  // 手动加锁
// 访问共享资源
++counter;
lock.unlock(); // 可以提前释放锁

这种机制适合在临界区较短或需要在加锁前后执行其他操作的场景。

避免死锁的常见技巧

多个互斥锁同时使用时容易发生死锁。例如线程A持有锁1并等待锁2,而线程B持有锁2并等待锁1。

解决方法包括:

  • 始终以相同的顺序获取多个锁
  • 使用 std::lock 一次性锁定多个互斥量,避免中间状态

示例:安全地锁定两个互斥量

std::mutex mtx1, mtx2;

void thread_func() {
    std::lock(mtx1, mtx2);                    // 同时尝试获取两个锁
    std::lock_guardzuojiankuohaophpcnstd::mutexyoujiankuohaophpcn lock1(mtx1, std::adopt_lock);
    std::lock_guardzuojiankuohaophpcnstd::mutexyoujiankuohaophpcn lock2(mtx2, std::adopt_lock);
    // 使用共享资源
}

这里 std::adopt_lock 表示构造 lock_guard 时不重新加锁,而是接管已持有的锁。

基本上就这些。只要合理使用 std::mutex 配合 RAII 风格的锁管理,就能写出安全高效的并发程序。关键是把临界区控制得越小越好,避免长时间持锁影响性能。