Std:: unique_lock <std:: mutex> или std:: lock_guard <std:: mutex>?
У меня есть два варианта использования.
а. Я хочу синхронизировать доступ двумя потоками к очереди.
В. Я хочу синхронизировать доступ двумя потоками к очереди и использовать переменную условия, потому что один из потоков будет ждать содержимого, которое будет храниться в очереди другим потоком.
В случае использования A я вижу пример кода, используя std::lock_guard<>
. Для случая использования B я вижу пример кода, используя std::unique_lock<>
.
В чем разница между двумя, и какой из них я должен использовать, в каком случае?
Ответы
Ответ 1
Разница в том, что вы можете заблокировать и разблокировать std::unique_lock
. std::lock_guard
будет заблокирован только один раз при построении и разблокирован при уничтожении.
Итак, для usecase B вам обязательно нужно std::unique_lock
для переменной условия. В случае А это зависит от необходимости блокировки охраны.
std::unique_lock
имеет другие функции, которые позволяют ему, например: быть сконструированы без блокировки мьютекса немедленно, но для создания обертки RAII (см. здесь).
std::lock_guard
также предоставляет удобную оболочку RAII, но не может безопасно блокировать несколько мьютексов. Его можно использовать, когда вам нужна оболочка для ограниченного объема, например: функция-член:
class MyClass{
std::mutex my_mutex;
void member_foo() {
std::lock_guard<mutex_type> lock(this->my_mutex);
/*
block of code which needs mutual exclusion (e.g. open the same
file in multiple threads).
*/
//mutex is automatically released when lock goes out of scope
};
Чтобы прояснить вопрос по chmike, по умолчанию std::lock_guard
и std::unique_lock
совпадают.
Итак, в приведенном выше случае вы можете заменить std::lock_guard
на std::unique_lock
. Однако std::unique_lock
может иметь более накладные расходы.
Ответ 2
lock_guard
и unique_lock
- почти то же самое; lock_guard
- ограниченная версия с ограниченным интерфейсом.
A lock_guard
всегда держит блокировку от ее конструкции до ее разрушения. A unique_lock
может быть создан без непосредственной блокировки, может разблокироваться в любой момент его существования и может передавать право собственности на блокировку из одного экземпляра в другой.
Таким образом, вы всегда используете lock_guard
, если вам не нужны возможности unique_lock
. A condition_variable
требуется unique_lock
.
Ответ 3
Используйте lock_guard
, если вам не нужно вручную unlock
отключить мьютекс, не разрушая lock
.
В частности, condition_variable
разблокирует свой мьютекс при переходе в режим ожидания при вызовах wait
. Вот почему lock_guard
здесь недостаточно.
Ответ 4
Существуют определенные общие вещи между lock_guard и unique_lock и некоторыми отличиями. Но в контексте заданного вопроса компилятор не разрешает использовать lock_guard в сочетании с переменной условия, потому что когда поток вызывает ожидание переменной условия, мьютекс автоматически разблокируется и когда другие потоки/потоки уведомляют и текущие thread вызывается (выходит из строя), блокировка повторно приобретается. Это явление противоречит принципу lock_guard. lock_guard можно построить только один раз и уничтожить только один раз.
Следовательно, lock_guard нельзя использовать в сочетании с переменной условия, но unique_lock может быть (поскольку unique_lock можно заблокировать и разблокировать несколько раз).
Ответ 5
Как уже упоминалось другими, std:: unique_lock отслеживает заблокированный статус мьютекса, поэтому вы можете отложить блокировку до завершения блокировки и разблокировать до уничтожения блокировки. std:: lock_guard не позволяет этого.
Кажется, нет причин, по которым функции ожидания std:: condition_variable не должны принимать lock_guard, а также уникальный_lock, потому что всякий раз, когда заканчивается ожидание (по какой-либо причине), мьютекс автоматически восстанавливается, чтобы не вызывать каких-либо семантических нарушений. Однако, согласно стандарту, для использования std:: lock_guard с переменной условия вам нужно использовать std:: condition_variable_any вместо std:: condition_variable.
Изменить: удалено "Использование интерфейса pthreads: std:: condition_variable и std:: condition_variable_any должны быть одинаковыми". При взгляде на реализацию gcc:
- std:: condition_variable:: wait (std:: unique_lock &) просто вызывает pthread_cond_wait() в базовой переменной условия pthread в отношении мьютекса, хранящегося в unique_lock (и поэтому может одинаково сделать то же самое для lock_guard, но doesn ' t, потому что стандарт не предусматривает этого)
- std:: condition_variable_any может работать с любым заблокируемым объектом, включая тот, который вообще не является блокировкой мьютекса (поэтому он может работать даже с межоперационным семафором)