Основанное на pthread событие, пробуждающее только ожидающие потоки - PullRequest
4 голосов
/ 28 мая 2009

В моей программе на C ++ у меня есть класс CEvent с функциями-членами триггера и ожидания, основанными на pthreads (работает в Linux). Реализация вполне очевидна (т.е. много примеров в сети), если есть один процесс ожидания. Однако теперь мне нужно выполнить требование о том, что несколько потоков ожидают события и должны ВСЕ надежно просыпаться при вызове trigger (). Второе условие: просыпаться должны только потоки, ожидающие при вызове trigger ().

Мой текущий код:

void CEvent::trigger() {
    pthread_mutex_lock(&mutex);
    wakeUp = true;
    pthread_cond_broadcast(&condition)
    pthread_mutex_unlock(&mutex);
    wakeUp = false;
}

void CEvent::wait() {
    pthread_mutex_lock(&mutex);
    while (!wakeUp)
        pthread_cond_wait(&condition, &mutex)

    pthread_mutex_unlock(&mutex);
}

Похоже, что это почти работает, поскольку все ожидающие потоки просыпаются, прежде чем я установлю для wakeUp значение false. Однако между трансляцией и сбросом wakeUp другие (или те же) потоки, вызывающие wait (), также сразу же проснутся, что недопустимо. Установка wakeUp = false до разблокировки мьютекста предотвращает пробуждение потоков.

Мои вопросы: * Когда возвращается pthread_cond_broadcast? То есть есть ли гарантия, что он вернется только после того, как все потоки проснулись, или он мог вернуться раньше? * Есть ли рекомендуемые решения этой проблемы?

1 Ответ

3 голосов
/ 28 мая 2009

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

Один из способов решения этой проблемы - использовать счетчик - каждый ожидающий поток будет увеличивать счетчик, а затем триггер будет ожидать, пока не проснется столько потоков, прежде чем возобновить работу. Затем вам нужно убедиться, что не ожидающим потокам не разрешено начинать ждать, пока это не произойдет.

// wake up "waiters" count of waiting threads
void CEvent::trigger()
{
    pthread_mutex_lock(&mutex);

    // wakey wakey
    wakeUp = true;
    pthread_cond_broadcast(&condition);

    // wait for them to awake
    while (waiters>0)
      pthread_cond_wait(&condition, &mutex);

    // stop waking threads up
    wakeUp = false;

    // let any "other" threads which were ready to start waiting, do so
    pthread_cond_broadcast(&condition);
    pthread_mutex_unlock(&mutex);
}

// wait for the condition to be notified for us
void CEvent::wait()
{
    pthread_mutex_lock(&mutex);

    // wait for us to be allowed to start waiting
    // we have to wait until any currrently being woken threads have gone
    while (wakeUp)
        pthread_cond_wait(&condition, &mutex);

    // our turn to start waiting
    waiters ++;

    // waiting
    while (!wakeUp)
        pthread_cond_wait(&condition, &mutex);

    // finished waiting, we were triggered
    waiters --;

    // let the trigger thread know we're done
    pthread_cond_broadcast(&condition);
    pthread_mutex_unlock(&mutex);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...