跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C++算法

C++ 多线程同步实战:互斥锁与死锁规避

C++ 多线程编程中,共享资源访问需通过互斥锁防止数据竞争。std::mutex 提供基础锁定能力,结合 std::lock_guard 可自动管理生命周期,避免异常导致的死锁。死锁常因循环等待锁产生,可通过固定获取顺序或 std::lock 规避。实战中如售票系统,利用锁保护临界区可确保逻辑正确性。

星落发布于 2026/3/23更新于 2026/5/33 浏览
C++ 多线程同步实战:互斥锁与死锁规避

C++ 多线程同步实战:互斥锁与死锁规避

在多线程环境下,多个线程同时访问共享资源是常态,但这往往也是 Bug 的温床。当两个线程同时对同一个变量进行读写操作时,如果没有适当的保护机制,最终结果往往会偏离预期。这种问题被称为数据竞争,解决它的核心手段就是线程同步。

为什么需要互斥锁?

想象一下两个线程都在执行全局变量 count 的自增操作。看似简单的 count++,在底层其实包含了读取、修改、写回三个步骤。如果线程 A 读到值后还没写完,线程 B 也读到了旧值,那么其中一个增量就会丢失。

运行下面的代码片段,你会发现最终结果通常小于预期的累加次数:

#include <iostream>
#include <thread>

using namespace std;

int count = 0;

void increment() {
    for (int i = 0; i < 100000; ++i) {
        count++; // 非原子操作,存在数据竞争
    }
}

int main() {
    thread t1(increment);
    thread t2(increment);
    t1.join();
    t2.join();
    cout << "最终 count 值:" << count << endl;
    return 0;
}

这就是典型的未同步场景。为了解决这个问题,我们需要引入互斥锁。

std::mutex 与 lock_guard 的正确用法

C++11 标准库中的 头文件提供了 std::mutex,这是最基础的互斥锁类型。它提供了 lock() 和 unlock() 接口,但手动管理锁的生命周期风险很大——一旦代码抛出异常,unlock() 可能无法执行,导致死锁。

因此,实际开发中更推荐配合 RAII 机制使用的 std::lock_guard。它在构造时自动加锁,析构时自动解锁,即使发生异常也能保证锁被释放。

改造上面的代码,加入互斥锁保护临界区:

#include 



  std;

 count = ;
mutex mtx; 

{
     ( i = ; i < ; ++i) {
        ; 
        count++;                       
    } 
}

{
    ;
    ;
    t();
    t();
    cout <<  << count << endl;
     ;
}
<iostream>
#include <thread>
#include <mutex>
using
namespace
int
0
// 定义全局互斥锁
void increment()
for
int
0
100000
lock_guard<mutex> lock(mtx)
// 自动加锁
// 临界区代码
// lock_guard 析构,自动解锁
int main()
thread t1(increment)
thread t2(increment)
1.
join
2.
join
"最终 count 值:"
return
0

加上锁之后,无论怎么运行,count 的值都会稳定在 200000。临界区的概念由此体现:同一时刻只有一个线程能进入这段代码。

死锁的产生与规避

在使用互斥锁时,除了数据竞争,另一个常见的大坑是死锁。简单来说,就是多个线程互相持有对方需要的锁,导致谁都无法继续执行。

死锁通常需要满足四个条件:互斥、请求与保持、不可剥夺、循环等待。一个经典的死锁示例是两个线程分别持有锁 A 和锁 B,却试图获取对方的锁:

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>

using namespace std;

mutex mtx1, mtx2;

void thread1() {
    mtx1.lock();
    this_thread::sleep_for(chrono::milliseconds(100));
    mtx2.lock(); // 等待 mtx2,此时 thread2 持有 mtx2 并等待 mtx1
    cout << "thread1 执行完毕" << endl;
    mtx2.unlock();
    mtx1.unlock();
}

void thread2() {
    mtx2.lock();
    this_thread::sleep_for(chrono::milliseconds(100));
    mtx1.lock(); // 等待 mtx1,此时 thread1 持有 mtx1 并等待 mtx2
    cout << "thread2 执行完毕" << endl;
    mtx1.unlock();
    mtx2.unlock();
}

int main() {
    thread t1(thread1);
    thread t2(thread2);
    t1.join();
    t2.join();
    return 0;
}

运行这段代码,程序会卡住不动。避免死锁有几种常用策略:

  1. 固定锁的获取顺序:所有线程都按相同顺序申请锁。
  2. 使用 std::lock:一次性获取多个锁,内部处理了死锁检测。
  3. 设置超时:使用 try_lock 或带超时的锁,防止永久阻塞。

实战案例:多线程售票系统

为了巩固理解,我们模拟一个简单的售票系统。多个窗口同时卖票,必须保证票数不会变成负数,也不会重复卖出。

#include <iostream>
#include <thread>
#include <mutex>
#include <vector>

using namespace std;

int tickets = 100; // 总票数
mutex mtx;         // 售票锁

void sell_tickets(int window_id) {
    while (true) {
        lock_guard<mutex> lock(mtx);
        if (tickets > 0) {
            cout << "窗口" << window_id << "售出第" << tickets << "张票" << endl;
            tickets--;
            this_thread::sleep_for(chrono::milliseconds(50)); // 模拟耗时
        } else {
            break;
        }
    }
    cout << "窗口" << window_id << "售票结束" << endl;
}

int main() {
    vector<thread> windows;
    // 创建 5 个售票窗口
    for (int i = 1; i <= 5; ++i) {
        windows.emplace_back(sell_tickets, i);
    }
    // 等待所有窗口售票结束
    for (auto& t : windows) {
        t.join();
    }
    cout << "所有票已售罄" << endl;
    return 0;
}

在这个案例中,互斥锁确保了 tickets 的检查和递减操作是原子的。最终你会看到 5 个窗口有序地卖完 100 张票,没有任何逻辑错误。

总结

多线程编程的核心在于平衡性能与安全。std::mutex 配合 std::lock_guard 是处理共享资源的标准方案。记住,锁不是越多越好,而是越精准越好。同时,时刻警惕死锁风险,遵循一致的锁获取顺序,能让你的并发代码更加健壮。

目录

  1. C++ 多线程同步实战:互斥锁与死锁规避
  2. 为什么需要互斥锁?
  3. std::mutex 与 lock_guard 的正确用法
  4. 死锁的产生与规避
  5. 实战案例:多线程售票系统
  6. 总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Python 月相可视化系统:从天文计算到 Web 界面生成
  • DICOM 标准详解:文件解析、Java/Python 库与 AI 应用
  • Neo4j Desktop 2 安装与使用指南
  • 基于 Obsidian 与 Claudian 构建本地 AI 知识库
  • 2026 前端跨端框架选型指南
  • 设计支持万人并发抢购的秒杀系统架构方案
  • 基于 Termux 在 Android 部署 OpenClaw 实现移动端 AI 助理
  • 前端加密靶场 encrypt-labs 环境搭建与全关卡解析
  • 基于 Python 的 Live2D 虚拟主播软件
  • DeepSeek 系列模型版本演进与核心特性对比
  • 基于 Go 构建可自我演进 AI 助手的架构设计与实践
  • Mac Mini 本地部署 OpenClaw 智能体实战指南
  • C++ 性能优化实战:从内存到 CPU 的执行效率提升
  • 多模态大模型垂直微调实战:Qwen3-VL-4B-Thinking 与 Llama Factory
  • Spring Boot 快速构建基于 Spring AI 的智能助手
  • ArrayList 与 LinkedList 性能实测:插入、遍历与删除耗时对比
  • Python 轻量级环境管理工具 UV 入门与 Windows 安装实战
  • 鸿蒙系统 PC 版安装与使用指南
  • OpenClaw Docker 部署:集成飞书钉钉 QQ 机器人的网关方案
  • C++ 继承机制详解:从基础到多继承与组合

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online