std::lock_guard
来自cppreference.com
在标头 <mutex> 定义 | ||
template<class Mutex > class lock_guard; | (C++11 起) | |
类 lock_guard
是互斥体包装器,为在作用域块期间占有互斥体提供便利的 RAII 风格机制。
当创建 lock_guard
对象时,它尝试接收给定互斥体的所有权。当控制离开创建 lock_guard
对象的作用域时,销毁 lock_guard
并释放互斥体。
lock_guard
类不可复制。
目录 |
[编辑]模板形参
Mutex | - | 要锁定的互斥体。类型必须满足可基本锁定(BasicLockable) 要求 |
[编辑]成员类型
成员类型 | 定义 |
mutex_type | Mutex |
[编辑]成员函数
构造 lock_guard ,可选地锁定给定的互斥体 (公开成员函数) | |
析构 lock_guard 对象,解锁底层互斥体 (公开成员函数) | |
operator= [弃置] | 不可复制赋值 (公开成员函数) |
[编辑]注解
一种常见的新手错误是忘记给 lock_guard
变量命名,例如 std::lock_guard{mtx}。这构造了一个纯右值对象并立即销毁,而并未真正为作用域的剩余部分构造持有互斥体的锁。
std::scoped_lock 给出 | (C++17 起) |
[编辑]示例
演示两个线程安全和不安全地增加一个 volatile 变量
运行此代码
#include <iostream>#include <mutex>#include <string_view>#include <syncstream>#include <thread> volatileint g_i =0;std::mutex g_i_mutex;// 保护 g_i void safe_increment(int iterations){const std::lock_guard<std::mutex> lock(g_i_mutex);while(iterations-->0) g_i = g_i +1;std::cout<<"线程 #"<<std::this_thread::get_id()<<", g_i: "<< g_i <<'\n'; // g_i_mutex 在锁离开作用域时自动释放} void unsafe_increment(int iterations){while(iterations-->0) g_i = g_i +1;std::osyncstream(std::cout)<<"线程 #"<<std::this_thread::get_id()<<", g_i: "<< g_i <<'\n';} int main(){auto test =[](std::string_view fun_name, auto fun){ g_i =0;std::cout<< fun_name <<":\n前, g_i: "<< g_i <<'\n';{std::jthread t1(fun, 1'000'000);std::jthread t2(fun, 1'000'000);}std::cout<<"后, g_i: "<< g_i <<"\n\n";}; test("safe_increment", safe_increment); test("unsafe_increment", unsafe_increment);}
可能的输出:
safe_increment: 前, g_i: 0 线程 #140121493231360, g_i: 1000000 线程 #140121484838656, g_i: 2000000 后, g_i: 2000000 unsafe_increment: 前, g_i: 0 线程 #140121484838656, g_i: 1028945 线程 #140121493231360, g_i: 1034337 后, g_i: 1034337
[编辑]缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 出版时的行为 | 正确行为 |
---|---|---|---|
LWG 2981 | C++17 | 曾提供来自 lock_guard<Mutex> 的冗余推导指引 | 已移除 |
[编辑]参阅
(C++11) | 实现可移动的互斥体所有权包装器 (类模板) |
(C++17) | 用于多个互斥体的免死锁 RAII 封装器 (类模板) |