C ++ 11: чому std :: condition_variable використовує std :: unique_lock?


74

Я трохи заплутаний у ролі, std::unique_lockколи працюю з 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оптимізації? Якщо так, то як саме це швидше?


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

1
"чому змінна умови використовує унікальний замок, а не мьютекс" це.
Лукас Клементе,

Відповіді:


104

тож немає технічної причини?

Я підтримав відповідь 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функцію-член для викриття мьютексу. *lk.release()було б тут згубно.

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


Не могли б ви пояснити "це". Ви говорите про lock_guard, чи condition_variable, чи, можливо condition_variable::wait?
Говард Хіннант,

Вибач, забудь. Я зовсім забув проcondition_variable_any
Стефана Доллберга

<nod> Ах, шаблон condition_variable::wait. Так, condition_variable_anyце шлях. І причина того, що функціональність не складена, condition_variableполягає в тому, що вона дорожча. І condition_variableце такий інструмент низького рівня, він повинен був бути якомога ефективнішим. Ви платите за додану функціональність, лише якщо використовуєте condition_variable_any.
Говард Хіннант,

Добре, дякую за додаткову інформацію. Можливо, додайте це до своєї відповіді, оскільки можна неправильно використати condition_variable_anyзвичайний мьютекс.
Stephan Dollberg

5
Ого, мене вразила ваша відповідь. Велике спасибі за цей рівень деталізації!
lucas clemente

35

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

Останніми роками фокус мови С ++ змістився на те, щоб зробити його безпечним за замовчуванням (але все одно дозволяючи користувачам забивати собі ноги, якщо вони цього хочуть, і намагатися достатньо).


тож немає технічної причини?
Лукас Клементе,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.