Как std :: condition_variable :: wait () оценивает данный предикат? - PullRequest
0 голосов
/ 25 февраля 2020

Контекст:

Во всех примерах, которые я могу видеть об использовании std::condition_variable::wait(), включая те, которые приходят с cppreference.com , синхронизации никогда не бывает механизм, используемый для защиты оценки предиката от гонок данных.

Например:

std::mutex m;
std::condition_variable cv;
int i = 0;

void waiting_func()
{
    std::unique_lock<std::mutex> lk(m);
    cv.wait(lk, [](){return i > 0;}); // No lock/unlock around the access of the global and shared variable i.
    // ...
}

Вопрос:

Если такой синхронизации нет, даже из авторитетных источников примеров, я думаю, это потому, что в этом нет необходимости. Но мне интересно, почему? Как std::condition_variable::wait() оценивает предикат так, чтобы он был потокобезопасным?

Мои мысли:

Я предложил две возможности:

  1. ) Либо, предикат гарантированно оценен атомарно (я никогда такого не читал, поэтому мой вопрос)
  2. ) Или, когда отправляется сигнал notify , функция std::condition_variable::wait() повторно запрашивает мьютекс перед оценкой предиката.

В случае точки 2.) , это может быть безопасно, если поток который изменяет i (и вызывает std::condition_variable::notify_one()), блокирует мьютекс m перед этим.

Например:

void modify_func()
{
    {
        std::scoped_lock<std::mutex> lk(m); // Acquire the mutex
        i += 1;                             // Modify i
    }                                       // Release the mutex
    cv.notify_one();
}

Конечно, другая возможность что мое понимание совершенно неверно, и тогда я упустил суть.

В любом случае, я действительно удивлен, что не смог найти никаких подробностей об этом в документации.

1 Ответ

1 голос
/ 25 февраля 2020

Ваш второй вариант верен. Как описано в cppreference , перегрузка предиката ведет себя как

while (!pred()) {
    wait(lock);
}

и wait(lock) всегда блокируется перед возвратом. См. wait постусловий в [thred.condition.condvar] / 12 и предикат поведения перегрузки в [thread.condition.condvar] / 15 стандарта C ++ 17 (черновик N4659).

И да, мьютекс должен быть заблокирован во время изменения i, даже если это атом c. См. Например этот вопрос .

...