std::scoped_lock

来自cppreference.com
< cpp‎ | thread
 
 
并发支持库
线程
(C++11)
(C++20)
(C++20)
this_thread 命名空间
(C++11)
(C++11)
(C++11)
互斥
(C++11)
(C++11)  
通用锁管理
(C++11)
(C++11)
scoped_lock
(C++17)
(C++11)
(C++11)
(C++11)
条件变量
(C++11)
信号量
闩与屏障
(C++20)
(C++20)
未来体
(C++11)
(C++11)
(C++11)
(C++11)
安全回收
(C++26)
(C++26)
风险指针





原子类型
(C++11)
(C++20)
原子类型的初始化
(C++11)(C++20 中弃用)
(C++11)(C++20 中弃用)
内存定序
原子操作的自由函数
原子标志的自由函数
 
 
在标头 <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,成员类型 mutex_typeMutex 相同,即 MutexTypes... 中的唯一类型。 否则,没有成员 mutex_type

成员函数

构造 scoped_lock,可选地锁定给定的互斥体
(公开成员函数)
析构 scoped_lock 对象,解锁底层互斥体
(公开成员函数)
operator=
[弃置]
不可复制
(公开成员函数)

注解

一种常见的新手错误,是“忘记”为 scoped_lock 变量命名,例如 std::scoped_lock(mtx);(默认构造了一个名为 mtxscoped_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...> 的冗余推导指引 已移除

参阅

实现可移动的互斥体所有权包装器
(类模板)
实现严格基于作用域的互斥体所有权包装器
(类模板)