2012-10-27 3 views
62

Я немного смущен о роли std::unique_lock при работе с std::condition_variable. Насколько я понял, documentation, std::unique_lock - это в основном раздутый замок, с возможностью переключения состояния между двумя замками.C++ 11: почему std :: condition_variable использует std :: unique_lock?

Я до сих пор использовал pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) для этой цели (я думаю, это то, что использует STL для posix). Он принимает мьютекс, а не блокировку.

В чем разница? Является ли тот факт, что std::condition_variable имеет дело с std::unique_lock оптимизацией? Если да, то как это происходит быстрее?

+2

Вы смущены тем, почему вам нужна блокировка/мьютекс с переменной состояния или разница между блокировкой и мьютексом или о том, почему переменная условия использует уникальный замок, а не мьютекс? – Brady

+0

«почему переменная условия использует уникальный замок, а не мьютекс». –

ответ

91

поэтому нет технической причины?

Я поддержал ответ cmeerw, потому что я считаю, что он дал техническую причину. Давайте пройдем через это. Давайте сделаем вид, что комитет решил провести condition_variable на mutex. Вот код, использующий эту конструкцию:

void foo() 
{ 
    mut.lock(); 
    // mut locked by this thread here 
    while (not_ready) 
     cv.wait(mut); 
    // mut locked by this thread here 
    mut.unlock(); 
} 

Это точно, как один не использовать condition_variable. В областях, отмеченных:

// mut locked by this thread here 

есть проблема с безопасностью, и это серьезная проблема. Если в этих областях выбрано исключение (или на cv.wait), заблокированное состояние мьютекса просачивается, если попытка try/catch также не помещается куда-нибудь, чтобы поймать исключение и разблокировать его. Но это всего лишь код, который вы просите программиста писать.

Предположим, что программист знает, как писать безопасный код исключения, и знает, как использовать unique_lock для его достижения. Теперь код выглядит так:

void foo() 
{ 
    unique_lock<mutex> lk(mut); 
    // mut locked by this thread here 
    while (not_ready) 
     cv.wait(*lk.mutex()); 
    // mut locked by this thread here 
} 

Это намного лучше, но это все еще не очень хорошая ситуация. Интерфейс condition_variable заставляет программиста уйти с пути, чтобы заставить работать. Существует возможное разыменование нулевого указателя, если lk случайно не ссылается на мьютекс. И нет никакого способа для condition_variable::wait, чтобы проверить, что этот поток имеет свой замок на mut.

О, просто помните, что существует опасность того, что программист может выбрать неправильную функцию-член unique_lock, чтобы выставить мьютексы. был бы катастрофическим.

Теперь давайте посмотрим на то, как код написан с фактическим condition_variable API, который принимает unique_lock<mutex>:

void foo() 
{ 
    unique_lock<mutex> lk(mut); 
    // mut locked by this thread here 
    while (not_ready) 
     cv.wait(lk); 
    // mut locked by this thread here 
} 
  1. Этот код так же просто, как он может получить.
  2. Это исключение.
  3. Функция wait может проверять lk.owns_lock() и выдавать исключение, если оно false.

Это технические причины, по которым дизайн API condition_variable.

Кроме того, condition_variable::wait не займет lock_guard<mutex>lock_guard<mutex>, потому что, как вы говорите: у меня есть замок на этом мьютекса до lock_guard<mutex> Уничтожает. Но когда вы вызываете condition_variable::wait, вы неявно освобождаете блокировку на мьютексе. Таким образом, действие несовместимо с оператором использования/.

Нам нужно unique_lock все равно так, что можно было бы вернуть замки из функций, поместите их в контейнеры, и блокировка/разблокировка мьютексы в не контекстными узорами в исключительных безопасным способом, так unique_lock был естественным выбором для condition_variable::wait.

Update

bamboon предложил в комментариях ниже, что я противоположность condition_variable_any, так что здесь идет:

Вопрос: Почему не condition_variable::wait шаблонных, так что я могу передать любой тип Lockable к нему ?

Ответ:

Это действительно здорово функциональность, чтобы иметь. Например, this paper демонстрирует код, ожидающий shared_lock (rwlock) в режиме совместного использования при переменной состояния (что-то неслыханное в мире posix, но очень полезно, тем не менее). Однако функциональность более дорогая.

Так комитет ввел новый тип с этой функциональностью:

`condition_variable_any` 

С этим condition_variableадаптером можно ждать любого заперты. Если у него есть члены lock() и unlock(), вам нужно идти. Для правильной реализации condition_variable_any требуется элемент данных condition_variable и элемент данных shared_ptr<mutex>.

Поскольку эта новая функциональность более дорогая, чем ваша базовая condition_variable::wait, а потому, что condition_variable - такой инструмент низкого уровня, эта очень полезная, но более дорогая функциональность была помещена в отдельный класс, так что вы платите только за нее, если используете ее ,

+0

Не могли бы вы прояснить «это». Вы говорите о 'lock_guard', или' condition_variable', или, возможно, 'condition_variable :: wait'? –

+0

Извините, забудьте об этом. Я полностью забыл о 'condition_variable_any' – inf

+0

А, шаблон' condition_variable :: wait'. Да, 'condition_variable_any' - это путь. И причина, по которой функциональность не сворачивается в 'condition_variable', заключается в том, что она дороже. И 'condition_variable' - такой инструмент низкого уровня, он должен быть как можно более эффективным. Вы платите только за добавленную функциональность, если используете 'condition_variable_any'. –

32

По сути, это решение для проектирования API, которое делает API максимально безопасным по умолчанию (при этом дополнительные служебные данные рассматриваются как незначительные). Требуется передать unique_lock вместо необработанных mutex пользователей API, направленных на написание правильного кода (при наличии исключений).

В последние годы фокус языка C++ сместился в сторону обеспечения его безопасности по умолчанию (но все же позволяет пользователям застревать в своих ногах, если они хотят и достаточно стараются).

+0

так нет технической причины? –

Смежные вопросы