std::scoped_lock
Определён в заголовке <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(присутствует при определённых условиях) | Если |
Функции-члены
создаёт scoped_lock, при необходимости блокируя переданные мьютексы (открытая функция-член) |
|
уничтожает объект scoped_lock, разблокируя лежащие в его основе мьютексы (открытая функция-член) |
|
|
operator=
[удалён] | копирующее присваивание не поддерживается (открытая функция-член) |
Примечания
Распространённая ошибка начинающих — «забыть» указать имя переменной 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 = "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)
{
std::osyncstream synced_out(std::cout);
synced_out << 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);
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");
// 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::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++.
| Отчёт об ошибке | Применён к | Опубликованное поведение | Исправленное поведение |
|---|---|---|---|
| LWG 2981 | C++17 | было предоставлено избыточное руководство по выводу типов из scoped_lock<MutexTypes...> | удалено |
См. также
|
(C++11) | реализует перемещаемую обёртку владения мьютексом (шаблон класса) |
|
(C++11) | реализует обёртку владения мьютексом, строго привязанную к области видимости (шаблон класса) |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/thread/scoped_lock