тож немає технічної причини?
Я підтримав відповідь cmeerw, оскільки, на мою думку, він дав технічну причину. Давайте пройдемося крізь нього. Давайте зробимо вигляд, що комітет вирішив condition_variable
почекати на mutex
. Ось код, що використовує такий дизайн:
void foo()
{
mut.lock();
while (not_ready)
cv.wait(mut);
mut.unlock();
}
Це саме те, як не слід використовувати condition_variable
. У регіонах, позначених:
є винятком проблема безпеки, і це серйозна проблема. Якщо в цих областях створюється виняток (або cv.wait
сам по собі), заблокований стан мьютексу витікає, якщо десь не встановлено функцію try / catch, щоб зловити виняток і розблокувати його. Але це просто більше коду, який ви просите програміста написати.
Скажімо, програміст знає, як написати безпечний код виключення, і знає, як його використовувати unique_lock
. Тепер код виглядає так:
void foo()
{
unique_lock<mutex> lk(mut);
while (not_ready)
cv.wait(*lk.mutex());
}
Це набагато краще, але ситуація все ще не є чудовою. condition_variable
Інтерфейс робить програміст вийти зі свого шляху , щоб отримати речі на роботу. Якщо lk
випадково не посилається на мьютекс, можливе розмежування нульового покажчика . І немає можливості condition_variable::wait
перевірити, чи є цей потік власним замком mut
.
О, щойно згадав, існує також небезпека того, що програміст може вибрати неправильну unique_lock
функцію-член для викриття мьютексу. *lk.release()
було б тут згубно.
Тепер давайте розглянемо, як код пишеться з фактичним condition_variable
API, який приймає unique_lock<mutex>
:
void foo()
{
unique_lock<mutex> lk(mut);
while (not_ready)
cv.wait(lk);
}
- Цей код настільки простий, наскільки він може отримати.
- Це виняток безпечний.
wait
Функція може перевірити lk.owns_lock()
і викинути виняток , якщо це false
.
Це технічні причини, які спричинили розробку API condition_variable
.
Крім того, condition_variable::wait
не приймає, lock_guard<mutex>
тому що lock_guard<mutex>
це те, як ви говорите: я володію блокуванням цього мьютексу, поки не lock_guard<mutex>
руйнуються. Але коли ви телефонуєте condition_variable::wait
, ви неявно звільняєте замок на мьютекс. Таким чином, ця дія не узгоджується із lock_guard
випадком використання / заявою.
Нам потрібно було в unique_lock
будь-якому випадку, щоб можна було повернути замки від функцій, помістити їх у контейнери та заблокувати / розблокувати мьютекси у незображених шаблонах у винятковий безпечний спосіб, так це unique_lock
був природний вибір для condition_variable::wait
.
Оновлення
bamboon запропонував у коментарях нижче, що я протиставляю condition_variable_any
, тому тут йдеться:
Запитання: Чому не складається condition_variable::wait
шаблон, щоб я міг здати будь-якийLockable
тип?
Відповідь:
Це справді крута функціональність. Наприклад, ця стаття демонструє код, який чекає на shared_lock
(rwlock) у спільному режимі із змінною умови (щось нечуване у світі posix, але тим не менш дуже корисне). Однак функціонал дорожчий.
Тож комітет представив новий тип із такою функціональністю:
`condition_variable_any`
За допомогою цього condition_variable
адаптера можна чекати будь-який тип, що замикається. Якщо в ньому є члени lock()
і unlock()
, то вам добре піти. Для належної реалізації condition_variable_any
необхідні condition_variable
член даних та shared_ptr<mutex>
член даних.
Оскільки ця нова функціональність є дорожчою, ніж ваша основна condition_variable::wait
, і оскільки condition_variable
це такий інструмент низького рівня, ця дуже корисна, але дорожча функціональність була виділена в окремий клас, так що ви платите за неї, лише якщо користуєтесь нею.