pthreads
Условные переменные
Поиск…
Вступление
Условные переменные полезны в тех случаях, когда вы хотите, чтобы поток ожидал чего-то, что происходит в другом потоке. Например, в сценарии производителя / потребителя с одним или несколькими производящими потоками и одним потребляющим потоком условные переменные могут использоваться для подачи сигнала потребляющему потоку, что новые данные доступны.
замечания
Общий процесс
Ожидание условной переменной (queueCond в примере производителя / потребителя) всегда связано с мьютексом (queueMutex в примере производителя / потребителя) и всегда должно быть связано с «нормальной» переменной состояния (queue.empty ( ) в примере производителя / потребителя). При правильном использовании это гарантирует отсутствие новых данных у потребителя.
В целом, процесс должен быть:
- Для сигнальной нити:
- Блокировка мьютекса
- Обновление любых данных и переменных состояния
- Сигнал переменной условия
- Разблокировать мьютексы
- Для ожидающего потока:
- Блокировка мьютекса
- Сделайте
whileцикл по переменному состоянию, зацикливание до тех пор , пока данные не готовы - В
whileцикл, сделать ожидание на условной переменной сpthread_cond_wait() - Когда в
whileцикл завершается, теперь мы уверены , что новые данные готовы, и что мьютекс заблокирован - Сделайте что-нибудь с данными
- Разблокировать мьютексы и повторить
С этой схемой, независимо от того, когда запланированы сигнальные и ожидающие потоки, ожидающий поток никогда не пропустит данные (так как в нем он никогда не застрянет в ожидании навсегда с готовностью готовых данных). Это может быть реализовано путем ручного запуска шагов для сигнального потока, записи состояний мьютекса, условий и переменных состояния для каждого из шагов в ожидающем потоке.
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);
}
}