тож немає технічної причини?
Я підтримав відповідь 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_variableAPI, який приймає 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це такий інструмент низького рівня, ця дуже корисна, але дорожча функціональність була виділена в окремий клас, так що ви платите за неї, лише якщо користуєтесь нею.