переменные условия в Linux, странное поведение - PullRequest
2 голосов
/ 18 марта 2010

Я синхронизирую процессы чтения и записи в Linux.

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

Для реализации механизма сна / пробуждения я использую значение условия Posix, pthread_cond_t. Клиенты вызывают pthread_cond_wait () для переменной для сна, в то время как сервер выполняет pthread_cond_broadcast (), чтобы разбудить их всех. Как сказано в руководстве, эти два вызова окружены блокировкой / разблокировкой соответствующего мьютекса pthread.

Переменная условия и мьютекс инициализируются на сервере и распределяются между процессами через общую область памяти (потому что я работаю не с потоками, а с отдельными процессами), и я уверен, что мое ядро ​​/ системный вызов поддерживают это ( потому что я проверил _POSIX_THREAD_PROCESS_SHARED).

Что происходит, так это то, что первый клиентский процесс спит и прекрасно просыпается. Когда я запускаю второй процесс, он блокирует свои функции pthread_cond_wait () и никогда не просыпается, даже если я уверен (по логам), что pthread_cond_broadcast () вызван.

Если я убиваю первый процесс и запускаю другой, он работает отлично. Другими словами, условная переменная pthread_cond_broadcast (), кажется, пробуждает только один процесс за раз. Если несколько и более процессов ожидают одну и ту же переменную общего условия, только первый из них удастся правильно разбудить, а другие, по-видимому, просто проигнорируют передачу.

Почему это поведение? Если я отправлю pthread_cond_broadcast (), каждый процесс ожидания должен проснуться, а не только один (и, тем не менее, не всегда один и тот же).

Ответы [ 4 ]

4 голосов
/ 18 марта 2010

Проверяете ли вы какое-либо условие, прежде чем ваш процесс на самом деле вызывает pthread_cond_wait()? Я спрашиваю, потому что это очень распространенная ошибка: ваш процесс должен не вызывать wait(), если вы не уверены, что какой-то процесс вызовет signal() (или broadcast()) позже .

учитывая этот код (из pthread_cond_wait справочной страницы):

          pthread_mutex_lock(&mut);
          while (x <= y) {
                  pthread_cond_wait(&cond, &mut);
          }
          /* operate on x and y */
          pthread_mutex_unlock(&mut);

Если вы пропустите тест while и просто отправите сигнал из другого процесса, когда ваше условие (x <= y) истинно, оно не будет работать, поскольку сигнал только пробуждает процесс, который уже ожидает. Если <code>signal() вызывается до того, как другой процесс вызовет wait(), сигнал будет потерян, и ожидающий процесс будет ждать вечно.

РЕДАКТИРОВАТЬ: О цикле while. Когда вы сигнализируете об одном процессе из другого процесса, он устанавливается в «список готовности», но не обязательно запланирован, и ваше состояние (x <= y) может снова измениться, поскольку никто не удерживает блокировку. Вот почему вы должны проверять свое состояние каждый раз, когда вы собираетесь ждать. Всегда должно быть <code>wakeup -> check if the condition is still true -> do work.

надеюсь, это понятно.

4 голосов
/ 18 марта 2010

Установили ли вы атрибут PTHREAD_PROCESS_SHARED как для condvar, так и для мьютекса?

Для Linux см. Следующие страницы man:

Методы, типы, константы и т. Д. Обычно определяются в /usr/include/pthread.h, /usr/include/nptl/pthread.h.

1 голос
/ 18 марта 2010

В документации сказано, что она должна работать ... Вы уверены, что это то же условное значение, на которое смотрят остальные потоки?

Это пример кода из opengroup.org :

pthread_cond_wait(mutex, cond):
    value = cond->value; /* 1 */
    pthread_mutex_unlock(mutex); /* 2 */
    pthread_mutex_lock(cond->mutex); /* 10 */
    if (value == cond->value) { /* 11 */
        me->next_cond = cond->waiter;
        cond->waiter = me;
        pthread_mutex_unlock(cond->mutex);
        unable_to_run(me);
    } else
        pthread_mutex_unlock(cond->mutex); /* 12 */
    pthread_mutex_lock(mutex); /* 13 */


pthread_cond_signal(cond):
    pthread_mutex_lock(cond->mutex); /* 3 */
    cond->value++; /* 4 */
    if (cond->waiter) { /* 5 */
        sleeper = cond->waiter; /* 6 */
        cond->waiter = sleeper->next_cond; /* 7 */
        able_to_run(sleeper); /* 8 */
    }
    pthread_mutex_unlock(cond->mutex); /* 9 */
0 голосов
/ 06 мая 2010

то, что сказал последний постер, правильно. Ключ ко всей ситуации с переменной cond, работающей правильно, состоит в том, что cond-var НЕ сигнализируется до того, как его ожидают. это строго сигнал, который должен использоваться, когда другие (одиночные или множественные) ждут. когда никто не ждет, это фактически NOP. что, кстати, не то, как я считаю, это должно работать, но как это работает.

Larry

...