Поиск…


Вступление

Условные переменные полезны в тех случаях, когда вы хотите, чтобы поток ожидал чего-то, что происходит в другом потоке. Например, в сценарии производителя / потребителя с одним или несколькими производящими потоками и одним потребляющим потоком условные переменные могут использоваться для подачи сигнала потребляющему потоку, что новые данные доступны.

замечания

Общий процесс

Ожидание условной переменной (queueCond в примере производителя / потребителя) всегда связано с мьютексом (queueMutex в примере производителя / потребителя) и всегда должно быть связано с «нормальной» переменной состояния (queue.empty ( ) в примере производителя / потребителя). При правильном использовании это гарантирует отсутствие новых данных у потребителя.

В целом, процесс должен быть:

  • Для сигнальной нити:
    1. Блокировка мьютекса
    2. Обновление любых данных и переменных состояния
    3. Сигнал переменной условия
    4. Разблокировать мьютексы
  • Для ожидающего потока:
    1. Блокировка мьютекса
    2. Сделайте while цикл по переменному состоянию, зацикливание до тех пор , пока данные не готовы
    3. В while цикл, сделать ожидание на условной переменной с pthread_cond_wait()
    4. Когда в while цикл завершается, теперь мы уверены , что новые данные готовы, и что мьютекс заблокирован
    5. Сделайте что-нибудь с данными
    6. Разблокировать мьютексы и повторить

С этой схемой, независимо от того, когда запланированы сигнальные и ожидающие потоки, ожидающий поток никогда не пропустит данные (так как в нем он никогда не застрянет в ожидании навсегда с готовностью готовых данных). Это может быть реализовано путем ручного запуска шагов для сигнального потока, записи состояний мьютекса, условий и переменных состояния для каждого из шагов в ожидающем потоке.

pthread_cond_wait и мьютексы

Чтобы облегчить вышеуказанный процесс, требуется вызвать pthread_cond_wait() с блокировкой мьютекса. При вызове pthread_cond_wait() затем разблокирует мьютекс, прежде чем помещать поток в режим сна, и, прежде чем вернуться по какой-либо причине, мьютекс будет заблокирован. Это также означает, что если какой-либо другой поток в настоящий момент заблокирован мьютексом, pthread_cond_wait() будет ждать разблокировки мьютекса и пока ожидающий поток не сможет фактически получить мьютекс - он будет бороться с ним вместе с любыми другими потоками, пытающимися заблокировать мьютекс одновременно.

Поддельные пробуждения

Кроме того , это может показаться, что в while цикл ожидания на переменном состоянии может быть заменен простым , if заявление. Тем не менее, в while цикл необходим, так как стандарт Posix позволяет pthread_cond_wait() , чтобы сделать так называемые «ложные» пробуждения во время ожидания, фактически не сигнализируется. Таким образом, код должен перепроверять переменную состояния, чтобы увидеть, возвращается ли pthread_cond_wait() из-за того, что на самом деле сигнализируется, или из-за одного из этих побочных пробуждений.

Пример производителя / потребителя

pthread_mutex_t queueMutex;
pthread_cond_t queueCond;
Queue queue;

void Initialize() {
    //Initialize the mutex and the condition variable
    pthread_mutex_init(&queueMutex, NULL);
    pthread_cond_init(&queueCond, NULL);
}

void Producer() {
    //First we get some new data
    Data *newData = MakeNewData();

    //Lock the queue mutex to make sure that adding data to the queue happens correctly
    pthread_mutex_lock(&queueMutex);

    //Push new data to the queue
    queue.push(newData);

    //Signal the condition variable that new data is available in the queue
    pthread_cond_signal(&queueCond);

    //Done, unlock the mutex
    pthread_mutex_unlock(&queueMutex);
}


void Consumer() {

    //Run the consumer loop
    while(1) {

        //Start by locking the queue mutex
        pthread_mutex_lock(&queueMutex);

        //As long as the queue is empty,
        while(queue.empty()) {
            // - wait for the condition variable to be signalled
            //Note: This call unlocks the mutex when called and
            //relocks it before returning!
            pthread_cond_wait(&queueCond, &queueMutex);
        }

        //As we returned from the call, there must be new data in the queue - get it,
        Data *newData = queue.front();
        // - and remove it from the queue
        queue.pop();

        //Now unlock the mutex
        pthread_mutex_unlock(&queueMutex);
        
        // - and process the new data
        ProcessData(newData);
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow