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)
scoped_lock
(C++17)
(C++11)
(C++11)
(C++11)(C++11)(C++11)
(C++11)
(C++11)
条件变量
(C++11)
信号量
闩与屏障
(C++20)
(C++20)
future
(C++11)
(C++11)
(C++11)
(C++11)
 
 
定义于头文件 <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) MutexMutexTypes... 中的单独类型

成员函数

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

示例

以下示例用 std::scoped_lock 锁定互斥对而不死锁,且为 RAII 风格。

#include <mutex>
#include <thread>
#include <iostream>
#include <vector>
#include <functional>
#include <chrono>
#include <string>
 
struct Employee {
    Employee(std::string id) : id(id) {}
    std::string id;
    std::vector<std::string> lunch_partners;
    std::mutex m;
    std::string output() const
    {
        std::string ret = "Employee " + id + " has lunch partners: ";
        for( const auto& partner : lunch_partners )
            ret += partner + " ";
        return ret;
    }
};
 
void send_mail(Employee &, Employee &)
{
    // 模拟耗时的发信操作
    std::this_thread::sleep_for(std::chrono::seconds(1));
}
 
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;
    }
 
    {
        // 用 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 << " 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::cout << alice.output() << '\n'  << bob.output() << '\n'
              << christina.output() << '\n' << dave.output() << '\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...> 的冗余推导指引 已移除

参阅

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