Condition_variable c++

Есть простой пример использования Condition_variable:

#include <iostream>           // std::cout
#include <thread>             // std::thread
#include <mutex>              // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable



std::mutex mtx;
std::condition_variable cv;
int global_status = 0;

void print_id(int id) 
{
    std::unique_lock<std::mutex> lck(mtx);


    while (global_status == 0)
    {
        cv.wait(lck);
    }


    std::cout << "thread " << id << '\n';
}


int main()
{
    std::thread threads[10];

    for (int i = 0; i < 10; ++i)
    {
        threads[i] = std::thread(print_id, i);
    }
  


    std::cout << "Start" << std::endl;


    std::this_thread::sleep_for(std::chrono::seconds(5));            //Пауза, чтобы убедится, что потоки в ожидании.

{
    std::unique_lock<std::mutex> lck(mtx);
    global_status = 1;
    cv.notify_all();
}

    for (auto& th : threads) th.join();

    return 0;
}

Я все таки до сих пор не могу понять, зачем блокировать переменную global_status, когда я меняю ее значение ?


Ответы (2 шт):

Автор решения: AlexGlebe

Между просмотром о состоянии переменной и командой ожидании сигнала находится критический участок кода.

Если поток ждёт сигнал, который был выслан до команды wait, то этот сигнал к нему так и не придёт. И будет ждать вечно. Сигнал получат только те процессы, что ждали его. А те процессы, что не успели встать в очередь будут ждать второго сигнала.

                   | while (global_status == 0)
global_status = 1; |
cv.notify_all();   |
                   | cv.wait(lck); вечно            

В примере не зря поставили задержку 5 секунд. Без неё сигналы получат те, кто успел. А блокирование для отправки сигнала препятствует хотя-бы такой ситуации.

В реальных программах, сначала ставиться мютекс, проверяется флаг, что процесс ждёт сигнал, и потом если надо - отправляет. Только потом освобождается мютекс. Без блокировки будет неразбериха.

→ Ссылка
Автор решения: avp

Condition работает следющим образом:

  1. при вызове .wait(mtx), переданный mutex освобождается, тем самым давая работать какому-то из потоков, ждущих указанного mutex-а, а сам thread приостанавливается.

  2. при вызове .notyfy_all() все потоки, приостановленные на condition variable, пробуждаются и тут же пытаются захватить mutex, который связан с этой переменной. Одному из них это удается и он возвращается из .wait() с захваченным mutex.

Т.е. mutex передаваемый в .wait() нужен для обеспечения сериализации работы потоков.


Возможно понимание этих мезанизмов будет лучше, если почитать документацию, например, по pthreads (обычно лежащих в основе С++ multithreading)

→ Ссылка