pthreads: pthread_cond_signal () из критического раздела

Я держу пари, что это не совсем, что Вы ищете, но:

у Кого-то здесь была та проблема, и его первая мысль была "эй, ocaml имеет довольно хороший!", и быстро портированный это к F#.

14
задан curryage 25 November 2009 в 18:37
поделиться

3 ответа

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

1
ответ дан 1 December 2019 в 13:09
поделиться

Обычно я рекомендую держать вызов 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 .)

17
ответ дан 1 December 2019 в 13:09
поделиться

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

Переменные условия обычно используются следующим образом:

#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 останется ждать - возможно, навсегда. Вот почему вы хотите подавать сигнал с заблокированным мьютексом.

2
ответ дан 1 December 2019 в 13:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: