std:: scoped_lock
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Member functions | ||||
|
定义于头文件
<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=
[deleted]
|
不可复制赋值
(公开成员函数) |
注释
一个常见的初学者错误是“忘记”为
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 <syncstream> #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&) { // 模拟耗时的消息操作 std::this_thread::sleep_for(1s); } void assign_lunch_partner(Employee& e1, Employee& e2) { std::osyncstream synced_out(std::cout); synced_out << e1.id << " and " << e2.id << " are waiting for locks" << 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); synced_out << 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"); // 在并行线程中分配,因为向用户发送午餐分配邮件耗时较长 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::osyncstream(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++标准。
| DR | 适用范围 | 发布时的行为 | 正确行为 |
|---|---|---|---|
| LWG 2981 | C++17 |
提供了来自
scoped_lock<MutexTypes...>
的冗余推导指南
|
已移除 |
参见
|
(C++11)
|
实现可移动的互斥量所有权包装器
(类模板) |
|
(C++11)
|
实现基于严格作用域的互斥量所有权包装器
(类模板) |