命名空间
变体
操作

std::scoped_lock

来自 cppreference.cn
< 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)
Hazard 指针
原子类型
(C++11)
(C++20)
原子类型的初始化
(C++11)(C++20 中已弃用)
(C++11)(C++20 中已弃用)
内存排序
(C++11)(C++26 中已弃用)
原子操作的自由函数
原子标志的自由函数
 
 
定义于头文件 <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,则成员类型 mutex_typeMutex 相同,MutexMutexTypes... 中的唯一类型。否则,没有成员 mutex_type

[编辑] 成员函数

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

[编辑] 注意

一个常见的初学者错误是“忘记”给 scoped_lock 变量命名,例如 std::scoped_lock(mtx); (这会默认构造一个名为 mtxscoped_lock 变量) 或 std::scoped_lock{mtx}; (这会构造一个立即销毁的 prvalue 对象),因此实际上并没有构造一个在作用域的剩余时间内持有互斥量的锁。

特性测试 Std 特性
__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++ 标准。

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

[编辑] 参见

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