У меня есть два варианта использования.
О. Я хочу синхронизировать доступ двумя потоками к очереди.
Б. Я хочу синхронизировать доступ двух потоков к очереди и использовать условную переменную, потому что один из потоков будет ожидать сохранения содержимого в очереди другим потоком.
Для варианта использования A я вижу пример кода, использующего std::lock_guard<>
. Для варианта использования B я вижу пример использования кода std::unique_lock<>
.
В чем разница между этими двумя и какой я должен использовать в каком случае использования?
Разница в том, что вы можете заблокировать и разблокировать std::unique_lock
, std::lock_guard
будет заблокирован только один раз на строительстве и разблокирован при уничтожении.
Так что для варианта использования 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
может быть немного больше накладных расходов.
Обратите внимание, что в эти дни следует использовать std::scoped_lock
вместо std::lock_guard
,
lock_guard
а также unique_lock
в значительной степени одно и то же; lock_guard
является ограниченной версией с ограниченным интерфейсом.
lock_guard
всегда держит замок от его строительства до его разрушения. unique_lock
может быть создан без немедленной блокировки, может разблокироваться в любой момент своего существования и может передавать право собственности на блокировку из одного экземпляра в другой.
Так что вы всегда используете lock_guard
, если вам не нужны возможности unique_lock
, condition_variable
нужен unique_lock
,
использование lock_guard
если вам не нужно иметь возможность вручную unlock
мьютекс между ними, не разрушая lock
,
Особенно, condition_variable
открывает свой мьютекс, когда ложится спать на звонки wait
, Вот почему lock_guard
здесь недостаточно
Есть определенные общие вещи между lock_guard
а также unique_lock
и определенные различия.
Но в контексте поставленного вопроса компилятор не позволяет использовать lock_guard
в сочетании с условной переменной, потому что, когда поток вызывает ожидание для условной переменной, мьютекс автоматически разблокируется, а когда другой поток / потоки уведомляют об этом, и текущий поток вызывается (выходит из режима ожидания), блокировка повторно устанавливается.
Это явление противоречит принципу lock_guard
, lock_guard
может быть построен только один раз и разрушен только один раз.
следовательно lock_guard
не может использоваться в сочетании с условной переменной, но unique_lock
может быть (потому что unique_lock
может быть заблокирован и разблокирован несколько раз).
Как было упомянуто другими, std :: unique_lock отслеживает заблокированное состояние мьютекса, поэтому вы можете отложить блокировку до завершения создания блокировки и разблокировать до разрушения блокировки. std :: lock_guard не разрешает это.
Кажется, нет причины, по которой функции ожидания std :: condition_variable не должны принимать lock_guard, а также unique_lock, потому что всякий раз, когда заканчивается ожидание (по какой-либо причине), мьютекс автоматически повторно запрашивается, чтобы не вызывать никакого семантического нарушения. Однако в соответствии со стандартом, чтобы использовать std :: lock_guard с условной переменной, вы должны использовать std :: condition_variable_any вместо std :: condition_variable.
редактировать: удалено «При использовании интерфейса pthreads std :: condition_variable и std :: condition_variable_any должны быть идентичны». Рассматривая реализацию gcc: