0

0

c++怎么使用std::scoped_lock避免死锁_c++ 17多互斥量安全锁定【教程】

穿越時空

穿越時空

发布时间:2026-01-01 14:11:02

|

161人浏览过

|

来源于php中文网

原创

std::scoped_lock比std::lock_guard更安全,因其支持多互斥量自动死锁避免、异常安全的统一加锁/解锁、构造失败自动回滚,且无需手动指定顺序。

c++怎么使用std::scoped_lock避免死锁_c++ 17多互斥量安全锁定【教程】

std::scoped_lock 能彻底避免死锁,但前提是所有线程按相同顺序请求互斥量——它本身不强制顺序,只保证原子性释放和异常安全。

为什么 std::scoped_lock 比 std::lock_guard 更安全

std::lock_guard 只支持单个互斥量;多个时必须手动调用 std::lock() + 多个 lock_guard,极易漏掉 unlock 或异常时析构不全。
std::scoped_lock 是 C++17 引入的“多互斥量 RAII 封装器”,内部自动调用 std::lock(使用死锁避免算法),且所有互斥量在构造时统一获取、析构时统一释放。

  • 构造失败(如被中断)时,已加锁的互斥量会自动回滚,不会残留锁定
  • 支持任意数量的互斥量类型(std::mutexstd::shared_mutex 等可混用)
  • 不依赖用户指定加锁顺序,内部用 std::lock 的试探-回退策略规避死锁

正确用法:构造即加锁,作用域结束自动解锁

直接把多个互斥量传给 std::scoped_lock 构造函数。它会在构造完成前确保全部加锁成功,否则抛出 std::system_error(如因中断失败)。

std::mutex mtx_a, mtx_b;
int data_a = 0, data_b = 0;

void transfer() {
    // 安全:自动处理 mtx_a 和 mtx_b 的加锁/解锁,包括异常路径
    std::scoped_lock lock(mtx_a, mtx_b);
    data_a += 10;
    data_b -= 10;
} // 这里自动 unlock(mtx_a) 和 unlock(mtx_b)
  • 不要提前声明再初始化:std::scoped_lock<:mutex std::mutex> lock; 不合法 —— 必须在构造时传入互斥量
  • 类型推导更简洁:std::scoped_lock lock(mtx_a, mtx_b); 编译器自动推导模板参数
  • 若需延迟构造(极少见),可用 std::defer_lock 配合 std::scoped_lock 的重载,但失去死锁防护优势

常见误用与陷阱

std::scoped_lock 不是万能解药。它无法防止逻辑级死锁(比如不同线程以不同顺序调用不同 scoped_lock 组合)。

Pi智能演示文档
Pi智能演示文档

领先的AI PPT生成工具

下载

立即学习C++免费学习笔记(深入)”;

  • 错误模式:thread1mtx_a 再锁 mtx_bthread2mtx_b 再锁 mtx_a —— 即使各自用 std::scoped_lock(mtx_a, mtx_b)std::scoped_lock(mtx_b, mtx_a),仍可能死锁(因为 std::lock 的算法在某些实现中不保证跨线程全局顺序)
  • 不支持超时:std::scoped_lock 没有类似 std::unique_lock::try_lock_for 的接口;需要超时请改用 std::unique_lock + std::try_lock
  • 不能转移或复制:std::scoped_lock 移动后原对象失效,且不可拷贝 —— 设计上就是绑定、作用域限定

和 std::unique_lock + std::lock 的对比

功能上,std::scoped_lock(mtx_a, mtx_b)std::unique_lock<:mutex> l1(mtx_a, std::defer_lock); std::unique_lock<:mutex> l2(mtx_b, std::defer_lock); std::lock(l1, l2);,但更简洁、更难出错。

  • 性能几乎无差别:两者底层都调用同一套 std::lock 实现
  • 可读性差异大:scoped_lock 一行表达意图;手动组合容易漏 std::defer_lock 或写错参数顺序
  • 维护成本低:后续增减互斥量只需改构造参数,不用同步调整多行 lock/unlock

真正容易被忽略的是:即使用了 std::scoped_lock,若系统中存在其他代码用裸 lock()try_lock()lock_guard 混用同一组互斥量,整体加锁顺序依然可能失控。安全的前提是统一约定、全项目收敛到一种多锁模式。

相关专题

更多
硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

989

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

50

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

222

2025.12.29

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

369

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

563

2023.08.10

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

471

2023.08.10

页面置换算法
页面置换算法

页面置换算法是操作系统中用来决定在内存中哪些页面应该被换出以便为新的页面提供空间的算法。本专题为大家提供页面置换算法的相关文章,大家可以免费体验。

389

2023.08.14

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

3

2025.12.31

php网站源码教程大全
php网站源码教程大全

本专题整合了php网站源码相关教程,阅读专题下面的文章了解更多详细内容。

1

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
国外Web开发全栈课程全集
国外Web开发全栈课程全集

共12课时 | 0.9万人学习

进程与SOCKET
进程与SOCKET

共6课时 | 0.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号