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
(условно присутствует) |
Если
sizeof...
(
MutexTypes
)
==
1
, тип члена
|
Функции-члены
создает объект
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...>
|
удалён |
Смотрите также
|
(C++11)
|
реализует перемещаемую обёртку владения мьютексом
(class template) |
|
(C++11)
|
реализует строго ограниченную областью видимости обёртку владения мьютексом
(class template) |