Namespaces
Variants

std:: scoped_lock

From cppreference.net
Concurrency support library
Threads
(C++11)
(C++20)
this_thread namespace
(C++11)
(C++11)
Cooperative cancellation
Mutual exclusion
Generic lock management
(C++11)
(C++11)
scoped_lock
(C++17)
(C++11)
(C++11)
Condition variables
(C++11)
Semaphores
Latches and Barriers
(C++20)
(C++20)
Futures
(C++11)
(C++11)
(C++11)
Safe reclamation
Hazard pointers
Atomic types
(C++11)
(C++20)
Initialization of atomic types
(C++11) (deprecated in C++20)
(C++11) (deprecated in C++20)
Memory ordering
(C++11) (deprecated in C++26)
Free functions for atomic operations
Free functions for atomic flags
定义于头文件 <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_type Mutex (即 MutexTypes... 中的唯一类型)相同。 否则不存在 mutex_type 成员。

成员函数

构造 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)
实现基于严格作用域的互斥量所有权包装器
(类模板)