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 , опционально блокируя указанные мьютексы
(public member function)
уничтожает объект scoped_lock , разблокирует базовые мьютексы
(public member function)
operator=
[deleted]
не копируемый через присваивание
(public member function)

Примечания

Распространённая ошибка новичков — «забыть» дать переменной scoped_lock имя, например: std :: scoped_lock ( mtx ) ; (что создаёт переменную scoped_lock с именем mtx по умолчанию) или std :: scoped_lock { mtx } ; (что создаёт prvalue-объект, который немедленно уничтожается), тем самым фактически не создавая блокировку, удерживающую мьютекс до конца области видимости.

Макрос тестирования возможностей Значение Стандарт Функция
__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 = "Сотрудник " + 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)
{
    std::osyncstream synced_out(std::cout);
    synced_out << e1.id << " и " << e2.id << " ожидают блокировок" << std::endl;
    {
        // Используйте std::scoped_lock для получения двух блокировок без опасений о
        // другие вызовы assign_lunch_partrier блокируют нас
        // а также предоставляет удобный механизм в стиле 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 << " и " << e2.id << " получил блокировки" << std::endl;
        e1.партнеры по обеду.push_back(e2.id);
        e2.партнеры по обеду.push_back(e1.id);
    }
    send_mail(e1, e2);
    send_mail(e2, e1);
}
int main()
{
    Employee alice("Алиса"), bob("Боб"), christina("Кристина"), 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.партнеры() << '\n'  
                                << bob.партнеры() << '\n'
                                << christina.партнеры() << '\n' 
                                << dave.партнеры() << '\n';
}

Возможный вывод:

Алиса и Боб ждут блокировок
Алиса и Боб получили блокировки
Кристина и Боб ждут блокировок
Кристина и Алиса ждут блокировок
Дэйв и Боб ждут блокировок
Дэйв и Боб получили блокировки
Кристина и Алиса получили блокировки
Кристина и Боб получили блокировки
Сотрудник Алиса имеет партнеров по обеду: Боб, Кристина
Сотрудник Боб имеет партнеров по обеду: Алиса, Дэйв, Кристина
Сотрудник Кристина имеет партнеров по обеду: Алиса, Боб
Сотрудник Дэйв имеет партнеров по обеду: Боб

Отчеты о дефектах

Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены ретроактивно к ранее опубликованным стандартам C++.

DR Применяется к Поведение в опубликованной версии Корректное поведение
LWG 2981 C++17 предоставлялся избыточный deduction guide из scoped_lock<MutexTypes...> удалён

Смотрите также

реализует перемещаемую обёртку владения мьютексом
(class template)
(C++11)
реализует строго ограниченную областью видимости обёртку владения мьютексом
(class template)