std::scoped_lock
来自cppreference.com
在标头 <mutex> 定义
|
||
template< class... MutexTypes > class scoped_lock; |
(C++17 起) | |
类 scoped_lock
是提供便利 RAII 风格机制的互斥包装器,它在作用域块的存在期间占有一或多个互斥体。
当创建 scoped_lock
对象时,它尝试取得各给定互斥体的所有权。当控制离开创建 scoped_lock
对象的作用域时,析构 scoped_lock
并释放各互斥体。若给出多个互斥体,则如同用 std::lock 使用免死锁算法。
scoped_lock
类不可复制。
模板形参
MutexTypes | - | 要锁定的各互斥体的类型。这些类型必须满足可锁定 (Lockable) 要求,除非 sizeof...(MutexTypes) == 1,该情况下唯一的类型必须满足可基本锁定 (BasicLockable) |
成员类型
成员类型 | 定义 |
mutex_type (有条件提供) |
若 sizeof...(MutexTypes) == 1,成员类型 |
成员函数
构造 scoped_lock ,可选地锁定给定的互斥体 (公开成员函数) | |
析构 scoped_lock 对象,解锁底层互斥体 (公开成员函数) | |
operator= [弃置] |
不可复制 (公开成员函数) |
注解
一种常见的新手错误,是“忘记”为 scoped_lock
变量命名,例如 std::scoped_lock(mtx);(默认构造了一个名为 mtx
的 scoped_lock
变量)或者 std::scoped_lock{mtx};(构造了一个右值对象并立即销毁),而并未实际创建在作用域的剩余部分中持有互斥体的锁。
功能特性测试宏 | 值 | 标准 | 功能特性 |
---|---|---|---|
__cpp_lib_scoped_lock |
201703L | (C++17) | std::scoped_lock
|
示例
以下示例用 std::scoped_lock
锁定互斥体对而不死锁,且为 RAII 风格。
运行此代码
#include <chrono> #include <functional> #include <iostream> #include <mutex> #include <string> #include <thread> #include <vector> using namespace std::chrono_literals; struct Employee { std::vector<std::string> lunch_partners; std::string id; std::mutex m; Employee(std::string id) : id(id) {} std::string partners() const { std::string ret = "雇员 " + id + " 的午餐伙伴: "; for (int count{}; const auto& partner : lunch_partners) ret += (count++ ? ", " : "") + partner; return ret; } }; void send_mail(Employee&, Employee&) { // 模拟耗时的发信操作 std::this_thread::sleep_for(1s); } void assign_lunch_partner(Employee& e1, Employee& e2) { static std::mutex io_mutex; { std::lock_guard<std::mutex> lk(io_mutex); std::cout << e1.id << " 和 " << e2.id << " 正等待锁定" << std::endl; } { // 用 std::scoped_lock 取得两个锁,而无需担心 // 其他对 assign_lunch_partner 的调用死锁我们 // 而且它亦提供便利的 RAII 风格机制 std::scoped_lock lock(e1.m, e2.m); // 等价代码 1(用 std::lock 和 std::lock_guard ) // std::lock(e1.m, e2.m); // std::lock_guard<std::mutex> lk1(e1.m, std::adopt_lock); // std::lock_guard<std::mutex> lk2(e2.m, std::adopt_lock); // 等价代码 2(若需要 unique_lock,例如对于条件变量) // std::unique_lock<std::mutex> lk1(e1.m, std::defer_lock); // std::unique_lock<std::mutex> lk2(e2.m, std::defer_lock); // std::lock(lk1, lk2); { std::lock_guard<std::mutex> lk(io_mutex); std::cout << e1.id << " 和 " << e2.id << " 获得了锁" << std::endl; } e1.lunch_partners.push_back(e2.id); e2.lunch_partners.push_back(e1.id); } send_mail(e1, e2); send_mail(e2, e1); } int main() { Employee alice("Alice"), bob("Bob"), christina("Christina"), dave("Dave"); // 在并行线程中指派,因为就午餐指派发邮件消耗很长时间 std::vector<std::thread> threads; threads.emplace_back(assign_lunch_partner, std::ref(alice), std::ref(bob)); threads.emplace_back(assign_lunch_partner, std::ref(christina), std::ref(bob)); threads.emplace_back(assign_lunch_partner, std::ref(christina), std::ref(alice)); threads.emplace_back(assign_lunch_partner, std::ref(dave), std::ref(bob)); for (auto& thread : threads) thread.join(); std::cout << alice.partners() << '\n' << bob.partners() << '\n' << christina.partners() << '\n' << dave.partners() << '\n'; }
可能的输出:
Alice 和 Bob 正等待锁定 Alice 和 Bob 获得了锁 Christina 和 Bob 正等待锁定 Christina 和 Alice 正等待锁定 Dave 和 Bob 正等待锁定 Dave 和 Bob 获得了锁 Christina 和 Alice 获得了锁 Christina 和 Bob 获得了锁 雇员 Alice 的午餐伙伴: Bob, Christina 雇员 Bob 的午餐伙伴: Alice, Dave, Christina 雇员 Christina 的午餐伙伴: Alice, Bob 雇员 Dave 的午餐伙伴: Bob
缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 出版时的行为 | 正确行为 |
---|---|---|---|
LWG 2981 | C++17 | 曾提供来自 scoped_lock<MutexTypes...> 的冗余推导指引
|
已移除 |
参阅
(C++11) |
实现可移动的互斥体所有权包装器 (类模板) |
(C++11) |
实现严格基于作用域的互斥体所有权包装器 (类模板) |