跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ 多线程同步:互斥锁 mutex 实战指南

C++ 多线程编程中共享资源访问常引发数据竞争。通过 std::mutex 互斥锁配合 std::lock_guard 可安全保护临界区。文章演示了死锁产生的四个条件及规避策略,如固定加锁顺序或使用 std::lock。结合售票系统案例,展示了如何确保票数统计准确,避免重复或负数情况。掌握这些机制对构建高并发稳定服务至关重要。

Elasticer发布于 2026/3/29更新于 2026/6/1519 浏览
C++ 多线程同步:互斥锁 mutex 实战指南

C++ 多线程同步:互斥锁 mutex 实战

在多线程环境下,多个线程同时读写共享资源时,很容易出现数据竞争。比如两个线程同时对一个全局变量做自增操作,最终结果往往小于预期。这是因为 count++ 并非原子操作,指令被交错执行了。解决这类问题的核心就是线程同步。

为什么需要互斥锁

C++11 引入了 <mutex> 头文件,提供了 std::mutex 类。最基础的接口包括 lock() 获取锁,unlock() 释放锁,以及 try_lock() 尝试获取但不阻塞。

手动调用 lock() 和 unlock() 有个隐患:如果中间抛出异常,unlock() 可能不会被执行,导致死锁。所以实际开发中,更推荐基于 RAII 机制的 std::lock_guard。它会在构造时自动加锁,析构时自动解锁,即使发生异常也能保证锁被释放。

实战:修复数据竞争

来看个例子。之前那个计数程序,加上互斥锁后逻辑就清晰了。

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

using namespace std;

int count = 0;
mutex mtx; // 定义全局互斥锁

void increment() {
    for (int i = 0; i < 100000; ++i) {
        lock_guard<mutex> lock(mtx); // 自动加锁
        count++; // 临界区代码,此时只有一个线程能执行
    }
    // lock_guard 析构,自动解锁
}

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

加上锁之后,count 的值就能稳定等于 200000。互斥锁保证了同一时刻只有一个线程能进入临界区。

警惕死锁

死锁是另一个常见坑。当多个线程互相持有对方需要的锁,且都在等待对方释放时,程序就会卡死。产生死锁通常需要满足四个条件:互斥、请求与保持、不可剥夺、循环等待。

下面这个场景就容易死锁:线程 1 拿了锁 A 等锁 B,线程 2 拿了锁 B 等锁 A。

#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();
    cout << "thread1 执行完毕" << endl;
    mtx2.unlock();
    mtx1.unlock();
}

void thread2() {
    mtx2.lock();
    this_thread::sleep_for(chrono::milliseconds(100));
    mtx1.lock();
    cout << "thread2 执行完毕" << endl;
    mtx1.unlock();
    mtx2.unlock();
}

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

运行这段代码,两个线程会互相等待,陷入死锁状态。

如何避免死锁

主要有几种办法。第一是固定锁的获取顺序,所有线程都按同一个顺序拿锁,比如先 mtx1 再 mtx2。第二是使用 std::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;
}

这样跑下来,5 个窗口有序售票,最终票数从 100 递减到 0,不会出现数据不一致的情况。

总结

多线程访问共享资源必须同步,否则会有数据竞争风险。std::mutex 搭配 std::lock_guard 是管理锁生命周期的最佳实践。死锁可以通过固定加锁顺序或使用 std::lock 来规避。核心原则就是保护好临界区,确保同一时刻只有一个线程在执行关键逻辑。

目录

  1. C++ 多线程同步:互斥锁 mutex 实战
  2. 为什么需要互斥锁
  3. 实战:修复数据竞争
  4. 警惕死锁
  5. 如何避免死锁
  6. 案例:多线程售票系统
  7. 总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • C++ 多线程同步实战:互斥锁(mutex)详解
  • C++ 多线程同步:互斥锁 mutex 实战指南
  • C++ 多态详解:从概念到实现原理
  • Foxglove 机器人开发环境完整搭建指南
  • TRAE IDE 使用指南:AI 原生开发环境入门
  • 字节 Seedance 2.0 AI 视频生成工具使用指南与渠道对比
  • C++ 多态详解:编译时与运行时多态原理及实现
  • ESP32-S3 部署 mimicLaw 集成 DeepSeek 与飞书机器人
  • C++ 多态的实现原理
  • 结合 Whisper 与 pyannote.audio 实现说话人分离转写系统
  • .net Core Web 保姆级教学 逐文件讲解 从0搭建一个 ASP.NET Core Razor Pages
  • 纯 CSS 实现简洁名片卡片设计实战
  • C++ 多态核心原理与实现机制
  • C++ 多态:概念、实现与底层原理详解
  • Llama-3.2V-11B-COT 部署:Triton 推理服务封装与压测
  • 双指针算法:四数之和解题思路与实现
  • C++ 多态核心原理与实现
  • C# 荣获 2023 年度编程语言奖,TIOBE 2024 年 1 月排行榜解读
  • C++ 内存区域详解:堆、栈与静态区变量存储
  • 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