命名空间
变体
操作

std::scoped_lock

来自 cppreference.com
< cpp‎ | thread
 
 
并发支持库
线程
(C++11)
(C++20)
this_thread 命名空间
(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++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 类不可复制。

内容

[edit] 模板参数

MutexTypes - 要锁定的互斥体的类型。这些类型必须满足 Lockable 要求,除非 sizeof...(MutexTypes) == 1,在这种情况下,唯一类型必须满足 BasicLockable

[edit] 成员类型

成员类型 定义
mutex_type
(有条件地存在)

如果 sizeof...(MutexTypes) == 1,成员类型 mutex_typeMutex 相同,MutexTypes... 中的唯一类型。否则,没有成员 mutex_type

[edit] 成员函数

构造一个 scoped_lock,可以选择锁定给定的互斥体
(公共成员函数) [edit]
析构 scoped_lock 对象,解锁底层互斥体
(公共成员函数) [edit]
operator=
[已删除]
不可复制赋值
(公共成员函数) [edit]

[edit] 注释

一个常见的初学者错误是“忘记”给 scoped_lock 变量命名,例如 std::scoped_lock(mtx);(它默认构造一个名为 mtxscoped_lock 变量)或 std::scoped_lock{mtx};(它构造一个立即被销毁的右值对象),因此实际上并没有构造一个锁,该锁在作用域的剩余部分保持互斥体的状态。

特性测试 Std 特性
__cpp_lib_scoped_lock 201703L (C++17) std::scoped_lock

[edit] 示例

以下示例使用 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

[edit] 缺陷报告

以下行为更改缺陷报告已追溯应用于以前发布的 C++ 标准。

DR 应用于 发布的行为 正确的行为
LWG 2981 C++17 scoped_lock<MutexTypes...> 提供了冗余的推断指南 已删除

[edit] 另请参阅

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