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 может работать с любым заблокируемым объектом, включая тот, который вообще не является блокировкой мьютекса (поэтому он может работать даже с межоперационным семафором)