Я держу пари, что это не совсем, что Вы ищете, но:
у Кого-то здесь была та проблема, и его первая мысль была "эй, ocaml имеет довольно хороший!", и быстро портированный это к F#.
Оба варианта верны, однако из-за проблем с реактивностью большинство планировщиков переключаются на другой поток при снятии блокировки. Если вы не сигнализируете перед разблокировкой, ваш ожидающий поток A отсутствует в списке готовности, и тысяча не будет запланирована до тех пор, пока B не будет снова запланирован и не вызовет pthread_cond_signal ().
Обычно я рекомендую держать вызов pthread_cond_signal ()
внутри заблокированной области, но, вероятно, не по тем причинам, которые вы думаете.
В большинстве случаев это не так. На самом деле не имеет значения, вызываете ли вы pthread_cond_signal ()
с удерживаемой блокировкой или нет. Бен прав в том, что некоторые планировщики могут принудительно переключать контекст при снятии блокировки, если есть другой ожидающий поток, поэтому ваш поток может быть отключен до того, как он сможет вызвать pthread_cond_signal ()
. С другой стороны, некоторые планировщики запускают ожидающий поток, как только вы вызываете pthread_cond_signal ()
, поэтому, если вы вызываете его с удерживаемой блокировкой, ожидающий поток проснется, а затем вернется в режим сна. (потому что теперь он заблокирован на мьютексе), пока сигнальный поток не разблокирует его. Точное поведение в значительной степени зависит от реализации и может меняться в зависимости от версии операционной системы, так что это не то, на что вы можете положиться.
Но все это выходит за рамки того, что должно быть вашей основной задачей, а именно удобочитаемости и правильности вашего кода. Вы вряд ли увидите реальный выигрыш в производительности от такого рода микрооптимизации (помните первое правило оптимизации: сначала профилируйте, а потом оптимизируйте). Однако легче думать о потоке управления, если вы знаете, что набор ожидающих потоков не может измениться между точкой, где вы задаете условие, и отправкой сигнала. В противном случае вы должны подумать о таких вещах, как «что, если поток A устанавливает testCondition = TRUE
и снимает блокировку, а затем поток B запускается и видит, что testCondition
истинно, и есть другие случаи (например, гонка, описанная выше), когда сигнал может привести к пробуждению потока, даже если условие не выполняется. В целях безопасности вам нужно поместить вызов pthread_cond_wait ()
в цикл while ()
, который проверяет условие, чтобы вы снова вызывали pthread_cond_wait ()
, если условие не выполняется после повторного получения блокировки. В вашем примере это будет выглядеть так:
pthread_mutex_lock(&my_lock);
while ( false == testCondition ) {
pthread_cond_wait(&my_wait,&my_lock);
}
pthread_mutex_unlock(&my_lock);
(Я также исправил то, что, вероятно, было опечаткой в вашем исходном примере, а именно использование my_mutex
для вызова pthread_cond_wait ()
вместо my_lock
.)
pthread_cond_wait ()
в цикл while ()
, который проверяет условие, чтобы вы снова вызывали pthread_cond_wait ()
, если условие не выполняется после повторного получения блокировки. В вашем примере это будет выглядеть так:
pthread_mutex_lock(&my_lock);
while ( false == testCondition ) {
pthread_cond_wait(&my_wait,&my_lock);
}
pthread_mutex_unlock(&my_lock);
(Я также исправил то, что, вероятно, было опечаткой в вашем исходном примере, а именно использование my_mutex
для вызова pthread_cond_wait ()
вместо my_lock
.)
pthread_cond_wait ()
в цикл while ()
, который проверяет условие, чтобы вы снова вызывали pthread_cond_wait ()
, если условие не выполняется после повторного получения блокировки. В вашем примере это будет выглядеть так:
pthread_mutex_lock(&my_lock);
while ( false == testCondition ) {
pthread_cond_wait(&my_wait,&my_lock);
}
pthread_mutex_unlock(&my_lock);
(Я также исправил то, что, вероятно, было опечаткой в вашем исходном примере, а именно использование my_mutex
для вызова pthread_cond_wait ()
вместо my_lock
.)
Поток, ожидающий переменной условия, должен держать мьютекс заблокированным, а другой поток всегда должен передавать сигнал с заблокированным мьютексом. Таким образом, вы знаете , что другой поток ожидает выполнения условия, когда вы отправляете сигнал. В противном случае возможно, что ожидающий поток не увидит сигнализируемое условие и заблокирует неопределенное время, ожидая его.
Переменные условия обычно используются следующим образом:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int go = 0;
void *threadproc(void *data) {
printf("Sending go signal\n");
pthread_mutex_lock(&lock);
go = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);
}
int main(int argc, char *argv[]) {
pthread_t thread;
pthread_mutex_lock(&lock);
printf("Waiting for signal to go\n");
pthread_create(&thread, NULL, &threadproc, NULL);
while(!go) {
pthread_cond_wait(&cond, &lock);
}
printf("We're allowed to go now!\n");
pthread_mutex_unlock(&lock);
pthread_join(thread, NULL);
return 0;
}
Это действительное :
void *threadproc(void *data) {
printf("Sending go signal\n");
go = 1;
pthread_cond_signal(&cond);
}
Однако рассмотрите, что происходит в main
while(!go) {
/* Suppose a long delay happens here, during which the signal is sent */
pthread_cond_wait(&cond, &lock);
}
. Если задержка, описанная в этом комментарии, произойдет, pthread_cond_wait
останется ждать - возможно, навсегда. Вот почему вы хотите подавать сигнал с заблокированным мьютексом.