尧图精选

C++ 多线程同步之互斥锁(mutex)实战指南

🕒 发布时间:2026/10/2 9:51:24 📁 来源:尧图网络
学习目标掌握 C 标准库中互斥锁的基本用法理解多线程同步的核心原理能够解决多线程环境下的资源竞争问题。学习重点std::mutex与std::lock_guard的使用、死锁的产生原因及规避方法、实际场景中的同步案例实现。48.1 多线程同步的必要性在多线程编程中当多个线程同时访问共享资源时会出现资源竞争问题。例如两个线程同时对同一个变量进行读写操作会导致最终结果与预期不符。这种问题被称为线程安全问题而解决该问题的核心就是线程同步。⚠️注意事项线程不同步会引发数据竞争造成程序运行结果不可预测甚至导致程序崩溃。举个简单的反例两个线程同时对全局变量count进行自增操作1234567891011121314151617#include iostream#include threadusingnamespacestd;intcount 0;voidincrement() {for(inti 0; i 100000; i) {count;// 非原子操作存在数据竞争}}intmain() {threadt1(increment);threadt2(increment);t1.join();t2.join();cout 最终 count 值 count endl;return0;}运行该程序会发现最终count的值大概率小于200000。这就是因为count不是原子操作被两个线程交替执行打乱了执行步骤。48.2 C 标准库中的互斥锁C11 及以后的标准库提供了mutex头文件封装了多种互斥锁相关的类。最基础且常用的就是std::mutex。48.2.1std::mutex的核心接口lock()获取互斥锁。如果锁已被其他线程占用当前线程会阻塞等待。unlock()释放互斥锁。必须与lock()成对使用。try_lock()尝试获取互斥锁。如果获取失败不会阻塞直接返回false。48.2.2std::lock_guard自动管理锁的生命周期直接使用lock()和unlock()容易出现遗漏解锁的情况。比如程序抛出异常时unlock()可能无法执行导致死锁。std::lock_guard基于RAII 机制实现可以自动在构造时加锁析构时解锁。✅核心结论实际开发中优先使用std::lock_guard而非手动调用lock()/unlock()。48.3 互斥锁实战解决数据竞争问题我们使用std::mutex和std::lock_guard改造 48.1 节的反例1234567891011121314151617181920#include iostream#include thread#include mutexusingnamespacestd;intcount 0;mutex mtx;// 定义全局互斥锁voidincrement() {for(inti 0; i 100000; i) {lock_guardmutex lock(mtx);// 自动加锁count;// 临界区代码此时只有一个线程能执行}// lock_guard 析构自动解锁}intmain() {threadt1(increment);threadt2(increment);t1.join();t2.join();cout 最终 count 值 count endl;return0;}运行该程序最终count的值稳定等于200000。这说明互斥锁成功保护了临界区代码避免了数据竞争。48.3.1 关键概念解释临界区需要被保护的、不能被多个线程同时执行的代码段。上例中count就是临界区。互斥锁的作用保证同一时刻只有一个线程能进入临界区。48.4 死锁的产生与规避死锁多个线程互相持有对方需要的锁导致所有线程都无法继续执行的状态。48.4.1 死锁的四个必要条件互斥条件资源只能被一个线程占用。请求与保持条件线程持有一个资源的同时请求其他线程持有的资源。不可剥夺条件线程已持有的资源不能被其他线程强制夺走。循环等待条件多个线程形成首尾相接的循环等待资源关系。48.4.2 死锁的示例两个线程分别持有一个锁同时请求对方的锁12345678910111213141516171819202122232425262728#include iostream#include thread#include mutexusingnamespacestd;mutex mtx1, mtx2;voidthread1() {mtx1.lock();this_thread::sleep_for(chrono::milliseconds(100));// 确保 thread2 先拿到 mtx2mtx2.lock();// 等待 mtx2此时 thread2 持有 mtx2 并等待 mtx1cout thread1 执行完毕 endl;mtx2.unlock();mtx1.unlock();}voidthread2() {mtx2.lock();this_thread::sleep_for(chrono::milliseconds(100));// 确保 thread1 先拿到 mtx1mtx1.lock();// 等待 mtx1此时 thread1 持有 mtx1 并等待 mtx2cout thread2 执行完毕 endl;mtx1.unlock();mtx2.unlock();}intmain() {threadt1(thread1);threadt2(thread2);t1.join();t2.join();return0;}运行该程序两个线程会互相等待陷入死锁状态无法输出任何内容。48.4.3 规避死锁的常用方法固定锁的获取顺序所有线程按照相同的顺序获取锁。比如上例中让两个线程都先获取mtx1再获取mtx2。使用std::lock同时获取多个锁std::lock可以一次性获取多个互斥锁避免循环等待。使用带超时的锁尝试通过try_lock()或std::timed_mutex在超时后放弃获取锁避免永久阻塞。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →