std::scoped_lock
来自 cppreference.cn
定义于头文件 <mutex> |
||
template< class... MutexTypes > class scoped_lock; |
(C++17 起) | |
scoped_lock
类是一个互斥体包装器,它提供了一种方便的 RAII 风格机制,用于在作用域块的持续时间内拥有零个或多个互斥体。
当创建 scoped_lock
对象时,它会尝试获取它所给定的互斥体的所有权。当控制离开创建 scoped_lock
对象的范围时,scoped_lock
会被销毁,并且互斥体会被释放。如果给定多个互斥体,则会使用死锁避免算法,如同通过 std::lock。
scoped_lock
类不可复制。
目录 |
[编辑] 模板参数
MutexTypes | - | 要锁定的互斥体类型。除非 sizeof...(MutexTypes) == 1,否则类型必须满足 Lockable 要求,在这种情况下,唯一的类型必须满足 BasicLockable |
[编辑] 成员类型
成员类型 | 定义 |
mutex_type (有条件存在) |
如果 sizeof...(MutexTypes) == 1,成员类型 |
[编辑] 成员函数
构造一个 scoped_lock ,可选地锁定给定的互斥体(public member function) | |
销毁 scoped_lock 对象,解锁底层互斥体(public member function) | |
operator= [已删除] |
不可复制赋值 (public member function) |
[编辑] 注意
一个常见的初学者错误是“忘记”给 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 = "Employee " + id + " has lunch partners: "; for (int count{}; const auto& partner : lunch_partners) ret += (count++ ? ", " : "") + partner; return ret; } }; void send_mail(Employee&, Employee&) { // Simulate a time-consuming messaging operation 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 << " and " << e2.id << " are waiting for locks" << std::endl; } { // Use std::scoped_lock to acquire two locks without worrying about // other calls to assign_lunch_partner deadlocking us // and it also provides a convenient RAII-style mechanism std::scoped_lock lock(e1.m, e2.m); // Equivalent code 1 (using std::lock and 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); // Equivalent code 2 (if unique_locks are needed, e.g. for condition variables) // 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 << " and " << e2.id << " got locks" << 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"); // Assign in parallel threads because mailing users about lunch assignments // takes a long time 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 and Bob are waiting for locks Alice and Bob got locks Christina and Bob are waiting for locks Christina and Alice are waiting for locks Dave and Bob are waiting for locks Dave and Bob got locks Christina and Alice got locks Christina and Bob got locks Employee Alice has lunch partners: Bob, Christina Employee Bob has lunch partners: Alice, Dave, Christina Employee Christina has lunch partners: Alice, Bob Employee Dave has lunch partners: Bob
[编辑] 缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 发布时的行为 | 正确的行为 |
---|---|---|---|
LWG 2981 | C++17 | 提供了冗余的 scoped_lock<MutexTypes...> 推导指南 |
已移除 |
[编辑] 另请参阅
(C++11) |
实现可移动的互斥体所有权包装器 (类模板) |
(C++11) |
实现严格基于作用域的互斥体所有权包装器 (类模板) |