Параллельное программирование на С++ в действии. Практика разработки многопоточных программ | страница 42
Что ж, это вовсе не сказка — именно такое поведение вы получаете при использовании примитива синхронизации, который называется мьютекс (слово mutex происходит от mutual exclusion — взаимное исключение). Перед тем как обратиться к структуре данных, программа захватывает (lock) мьютекс, а по завершении операций с ней освобождает (unlock) его. Библиотека Thread Library гарантирует, что если один поток захватил некоторый мьютекс, то все остальные потоки, пытающиеся захватить тот же мьютекс, будут вынуждены ждать, пока удачливый конкурент не освободит его. В результате все потоки видят согласованное представление разделяемых данных, без нарушенных инвариантов.
Мьютексы — наиболее общий механизм защиты данных в С++, но панацеей они не являются; важно структурировать код так, чтобы защитить нужные данные (см. раздел 3.2.2), и избегать состояний гонки, внутренне присущих интерфейсам (см раздел 3.2.3). С мьютексами связаны и собственные проблемы, а именно: взаимоблокировки (deadlock) (см. раздел 3.2.4), а также защита слишком большого или слишком малого количества данных (см. раздел 3.2.8). Но начнем с простого.
3.2.1. Использование мьютексов в С++
В С++ для создания мьютекса следует сконструировать объект типа >std::mutex
, для захвата мьютекса служит функция-член >lock()
, а для освобождения — функция-член >unlock()
. Однако вызывать эти функции напрямую не рекомендуется, потому что в этом случае необходимо помнить о вызове >unlock()
на каждом пути выхода из функции, в том числе и вследствие исключений. Вместо этого в стандартной библиотеке имеется шаблон класса >std::lock_guard
, который реализует идиому RAII — захватывает мьютекс в конструкторе и освобождает в деструкторе, — гарантируя тем самым, что захваченный мьютекс обязательно будет освобожден. В листинге 3.1 показано, как с помощью классов >std::mutex
и >std::lock_guard
защитить список, к которому могут обращаться несколько потоков. Оба класса определены в заголовке >
.
Листинг 3.1. Защита списка с помощью мьютекса
>#include
>#include
>#include
>std::list
(1)
>std::mutex some_mutex; ←
(2)
>void add_to_list(int new_value) {
> std::lock_guard