2013-05-27 3 views
5

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

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

Чтобы устранить эту проблему, урезанная мой код, чтобы только два потока, выполняющих блокировки/разблокировки/сигнализации, которая приведена ниже:

#include <stdio.h> 
#include <pthread.h> 
#include <stdbool.h> 

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; 
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; 
pthread_cond_t condition1 = PTHREAD_COND_INITIALIZER; 
pthread_cond_t condition2 = PTHREAD_COND_INITIALIZER; 
bool predicate1 = false; 
bool predicate2 = false; 

static void * ThreadFunc2(void * arg) { 
    sleep(1); // For testing purposes, ensures this thread is run after Thread1 

    pthread_mutex_lock(&mutex2); 
    while(1) { 
     pthread_mutex_lock(&mutex1); 
     // Do some work - Eg receive some data from a socket 
     predicate1 = false; 
     pthread_cond_signal(&condition1); 
     pthread_mutex_unlock(&mutex1); 

     predicate2 = true; 
     while(predicate2 == true) 
      pthread_cond_wait(&condition2, &mutex2); 

     // Do some more work - Eg send response data to socket 
    } 
} 

static void * ThreadFunc1(void * arg) { 
    int result; 

    pthread_mutex_lock(&mutex1); 
    while(1) { 
     predicate1 = true; 
     while(predicate1 == true) 
      pthread_cond_wait(&condition1, &mutex1); 

     // Do some work - Eg process data on the socket and prepare response data to be sent 
     pthread_mutex_lock(&mutex2); 
     predicate2 = false; 
     pthread_cond_signal(&condition2); 
     pthread_mutex_unlock(&mutex2); 
    } 
} 

int main(int argc, char * argv[]) { 
    pthread_t thread1Id, thread2Id; 

    pthread_create(&thread1Id, NULL, ThreadFunc1, NULL); 
    pthread_create(&thread2Id, NULL, ThreadFunc2, NULL); 

    while(1) { 
     sleep(1); 
    } 

    return 0; 
} 

Если исключить все заявления, относящиеся к mutex2/condition2/predicate2, то два потока работают вместе, как ожидалось.

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

Также, чтобы помочь мне отлаживать, я переопределил функции pthread_ *, чтобы напечатать сообщение для stdout с соответствующими номерами строк до вызова фактических функций pthread_ *. Это позволило мне следить за потоком каждой операции pthread и убедиться, что сигнал отправляется в состояние ожидания.

Может ли кто-нибудь помочь мне пролить свет на любые потенциальные проблемы, которые могут присутствовать в моей реализации выше?

Заранее благодарим за любые предложения.

+0

Ваш примерный код никогда не устанавливает 'predicate2' в' false'. – caf

+0

Я добавил отсутствующую инструкцию predicate2, как отметил @caf - Спасибо. К сожалению, этот вопрос все еще остается. – wei

+0

Можете ли вы опубликовать полную тестовую программу (похоже, ее не должно быть намного больше), команда, используемая для создания программы и информации о используемой версии платформы и инструментов? Я не могу воспроизвести с достаточным количеством лесов, добавленных для запуска потоков и нескольких вызовов 'puts()', чтобы убедиться, что прогресс уже сделан. Это на x86 Linux 3.0 с gcc 4.6.1. –

ответ

1

У меня возникли аналогичные проблемы. В моем случае иногда сигнал посылался до того, как заблокированный поток ожидал. Поведение в таком случае состояло в том, что оба потока были «застреваны». Мы решили это, добавив флаг, уведомляющий о передаче сигнала.

+0

Как вам удалось просыпать ожидающую нить, когда она застряла в pthread_cond_wait? Используете ли вы timedwait вместо wait и проверяете переменную флага так же, как и предикатные переменные, которые я использовал выше? Благодарю. – wei

+0

Если флаг установлен, он не дождался, просто продолжил. Таким образом, мы не пропустили сигнал. – eyalm

2

Ваша ошибка в том, что вы не разблокируете мьютексы, используемые переменной условия после вызовов pthread_cond_wait().

например, pthread_cond_wait() разблокирует мьютекс внутри, пока поток заблокирован, но он повторно приобретает блокировку при просыпании и вам необходимо явно освободить ее.

См. Этот учебник для получения дополнительной информации о cond. переменные: https://computing.llnl.gov/tutorials/pthreads/#ConditionVariables

+1

Спасибо за ваш ответ. Я не разблокировал каждый мьютекс после каждого ожидания, так как нет зависимостей от соответствующего мьютекса до тех пор, пока цикл не перезапустится. Из моего понимания (что может быть некорректно) единственным требованием для оператора wait является то, что мьютекс уже должен быть заблокирован текущим потоком, который имеет место, когда каждый поток выполняет инструкцию pthread_cond_wait. Существуют ли дополнительные требования, которые я пропустил, например, требуя, чтобы мьютексы были выпущены и повторно получены до следующего заявления pthread_cond_wait? Благодарю. – wei

+0

В этом коде нет необходимости разблокировать эти мьютексы. – caf

+0

@wei, что удерживает компилятор от сохранения предиката2 в регистре? если вы приобретете и отпустите мьютекс, вы бы пошли через барьер памяти компилятора. Я не уверен, что pthread_cond_wait() - это барьер памяти. – gby

0

Solution - см объяснение ниже
Ввод pthread_mutex_unlock() перед pthread_cond_signal сигнализации вызова() вместо того, чтобы после того, как он должен решить вопрос

... 
pthread_mutex_lock(&mutex1); 
predicate1 = false; 
pthread_mutex_unlock(&mutex1); 
pthread_cond_signal(&condition1); 
... 

в функции ThreadFunc2 и аналогично для нить 1

... 
pthread_mutex_lock(&mutex2); 
predicate2 = true; 
pthread_mutex_unlock(&mutex2); 
pthread_cond_signal(&condition2); 
... 

в функции ThreadFunc1.

Объяснение В вашей программе нить 2 приходит в сигнализации вызова

pthread_cond_signal(&condition1); // thread 2 with mutex1 locked 

с mutex1 запертой. Тема 1 может оставить только блокирующий

pthread_cond_wait(&mutex1);  // thread 1 leaves only after mutex1 unlocked 

вызова самого замка mutex1 который является гарантированным поведением этого вызова функции - это значит, что она должна быть разблокирована всеми другими нитями, чтобы продолжить. Если у вас есть реализация pthread_cond_signal(), которая блокируется до тех пор, пока поток, который принимает сигнал, не продолжит, тогда мертвая блокировка будет получена, когда она войдет в вызов с блокировкой соответствующего мьютекса. Это также может объяснить, почему одна среда может работать нормально, а другая - нет: например, когда ваша среда рабочего стола не имеет блокирующего вызова pthread_cond_signal(), в то время как ваша встроенная среда делает это.

Смежные вопросы