2009-03-22 6 views
5

Estoy trabajando en un proyecto y trato de usar pthread_cond_wait() y pthread_cond_signal() para sincronizar dos hilos.Señal condicional Pthread: no funciona como se esperaba

Mi código es como la siguiente:

pthread_mutex_t lock_it = PTHREAD_MUTEX_INITIALIZER; 
pthread_cond_t write_it = PTHREAD_COND_INITIALIZER; 

    int main(int argc, char**argv) 
    { 

    pthread_t t_send_segments, t_recv_acks; 

    pthread_create(&t_send_segments, NULL, send_segments, (void*)NULL); 
    pthread_create(&t_recv_acks,  NULL, recv_acks,  (void*)NULL); 

    pthread_join(t_recv_acks, (void**)NULL); 

    pthread_mutex_destroy(&lock_it); 
    pthread_cond_destroy(&write_it); 
} 

void* send_segments(void *v) { 
    for(;;) { 
     pthread_mutex_lock(&lock_it); 
     printf("s1\n"); 
     printf("s2\n"); 
     pthread_cond_wait(&write_it, &lock_it); 
     printf("s3\n"); 
     printf("s4\n"); 
     printf("s5\n"); 
     pthread_mutex_unlock(&lock_it); 
    } 
    return 0; 
} 

void* recv_acks(void *v) { 
    for(;;) { 
     pthread_mutex_lock(&lock_it); 
     printf("r1\n"); 
     pthread_cond_signal(&write_it); 
     printf("r2\n"); 
     pthread_mutex_unlock(&lock_it); 
    } 
    return 0; 
} 

El resultado esperado es:

s1 
s2 
r1 
s3 
s4 
s5 
s1 
s2 
r2 
r1 
s3 
s4 
s5 

(etc) 

Mi salida no sigue este patrón en absoluto. Claramente tengo un error de lógica en alguna parte, pero no estoy entendiendo dónde. ¿Por qué no el recv_acks() hilo siempre cede cuando llega al pthread_cond_signal() - ya que el pthread_cond_wait() siempre se ejecuta primero (por el orden en el que creo los hilos) y el cond_wait() siempre se ejecuta desde su en la sección crítica?

+0

¿Cómo se ve tu salida? – Hasturkun

+0

"siempre se ejecuta primero (debido al orden en el que creo los hilos" - confiar en esto puede ser bastante peligroso. Tome el mutex antes de iniciar los hilos y pase la propiedad al remitente. – Dipstick

Respuesta

8

El pthread_cond_signal la función no hace que el hilo actual ceda y no libera el mutex. Todo lo que hace es reiniciar un hilo que se ha suspendido con la condición a través del pthread_cond_wait. Esto solo significa que el hilo despertado está disponible para la programación, no hace que se ejecute inmediatamente. El programador de hilos lo programará en algún momento en el futuro.

Además, solo porque el subproceso se ha activado y compite por el mutex, eso no significa que va a obtener el mutex siguiente. Los mutexes no son necesariamente justos para todos los hilos que lo han solicitado. De acuerdo con la página del manual pthread_mutex: "pthread_mutex_lock bloquea el mutex dado. Si el mutex está actualmente desbloqueado, se convierte en bloqueado y propiedad de la cadena de llamada, y pthread_mutex_lock lo devuelve inmediatamente". Por lo tanto, el subproceso en "r" puede girar en su ciclo varias veces, desbloqueando y volviendo a bloquear felizmente el mutex varias veces antes de ser intercambiado por el planificador. Esto significa que el subproceso s solo tendrá una oportunidad en el mutex si el planificador interrumpe el subproceso durante el breve tiempo en que ha liberado el mutex.

Para lograr la salida que desea, ambos hilos necesitarán controlar su ejecución con una condición y señal entre sí antes de suspenderse. Sin embargo, esto puede o no ser lo que realmente quiere hacer con su proyecto real.

Otra nota: en realidad no importa en qué orden haya creado los hilos. La creación de un hilo no genera el hilo de creación. Entonces, el hilo principal probablemente creará ambos hilos antes de que se programe, y el programador de hilos será libre de programar cualquiera de ellos para su ejecución siguiente. Si el subproceso s se ejecuta primero en su plataforma, ese es simplemente el comportamiento de implementación en su plataforma y no es algo en lo que se deba confiar.

6

Parece que recuerdo haber leído en algún lugar que pthread_cond_signal() en realidad no hace que el hilo ceda inmediatamente. Dado que pthread_cond_signal() no libera el bloqueo, el hilo que lo llama debe continuar ejecutándose hasta que libera el bloqueo, y solo luego cede y permite que el hilo señalado regrese de pthread_cond_wait().

Si ese es el caso, entonces creo que su salida es parecida a

s1 
s2 
r1 
r2 
s3 
s4 
s5 

etc .... si no es eso, es posible que tenga que editar la salida real en su pregunta para obtener una respuesta precisa .

1

Creo que su problema proviene de tratar de usar un bloqueo para demasiadas cosas. Solo debe usar un candado por 1 cosa, de esa manera no hay confusión sobre lo que está esperando. Sugiero agregar un segundo bloqueo para la señal de escritura. Además, debe agregar un segundo cond_wait para la segunda agrupación de mensajes. Si no lo haces, el orden en el que se ejecutarán las cosas será aleatorio. Aquí está mi versión editada de su programa:

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

#define NUM_MESSAGES 3 
int messages = 0; 
void * send_segments(void *); 
void * recv_acks(void *v); 
pthread_mutex_t lock_it = PTHREAD_MUTEX_INITIALIZER; 
pthread_mutex_t write_mutex = PTHREAD_MUTEX_INITIALIZER; 
pthread_cond_t write_cond = PTHREAD_COND_INITIALIZER; 

    int main(int argc, char**argv) 
    { 

    pthread_t t_send_segments, t_recv_acks; 

    pthread_create(&t_send_segments, NULL, send_segments, (void*)NULL); 
    pthread_create(&t_recv_acks,  NULL, recv_acks,  (void*)NULL); 

    pthread_join(t_recv_acks, (void**)NULL); 
    pthread_join(t_send_segments, (void**)NULL); 

    //pthread_mutex_destroy(&lock_it); 
    //pthread_cond_destroy(&write_it); 
} 

void* send_segments(void *v) { 
    for(;;) { 
     pthread_mutex_lock(&lock_it); 
     printf("s1\n"); 
     printf("s2\n"); 
     pthread_mutex_unlock(&lock_it); 

     // wait for other thread 
     pthread_mutex_lock(&write_mutex); 
     pthread_cond_wait(&write_cond, &write_mutex); 
     pthread_mutex_unlock(&write_mutex); 

     pthread_mutex_lock(&lock_it); 
     printf("s3\n"); 
     printf("s4\n"); 
     printf("s5\n"); 
     pthread_mutex_unlock(&lock_it); 

     // wait for other thread 
     pthread_mutex_lock(&write_mutex); 
     pthread_cond_wait(&write_cond, &write_mutex); 
     pthread_mutex_unlock(&write_mutex); 

     if (messages > NUM_MESSAGES) return(NULL); 
    } 
    return 0; 
} 


void* recv_acks(void *v) { 
    for(;;) { 
     // write first response 
     pthread_mutex_lock(&lock_it); 
     printf("r1\n"); 
     pthread_mutex_unlock(&lock_it); 

     // signal other thread 
     pthread_mutex_lock(&write_mutex); 
     pthread_cond_signal(&write_cond); 
     pthread_mutex_unlock(&write_mutex); 

     // write second response 
     pthread_mutex_lock(&lock_it); 
     printf("r2\n\n"); 
     // increment count before releasing lock, otherwise the other thread 
     // will be stuck waiting for a write_cond signal 
     messages++; 
     pthread_mutex_unlock(&lock_it); 

     // signal other thread 
     pthread_mutex_lock(&write_mutex); 
     pthread_cond_signal(&write_cond); 
     pthread_mutex_unlock(&write_mutex); 

     if (messages > NUM_MESSAGES) return(NULL); 
    } 
    return 0; 
} 

Si ejecuta este programa, me sale el siguiente resultado (nótese que he añadido una segunda nueva línea después de r2 para mayor claridad):

[email protected]:~/tmp$ ./test 
s1 
s2 
r1 
s3 
s4 
s5 
r2 

s1 
s2 
r1 
s3 
s4 
s5 
r2 

s1 
s2 
r1 
s3 
s4 
s5 
r2 

s1 
s2 
r1 
s3 
s4 
s5 
r2 

[email protected]:~/tmp$ 
+0

Ejecuté este código que publicó y no lo hace . obtener los mismos resultados a medida que parece como si mi recibir hilo obtiene programada primero y el programa simplemente se cuelga: [ubuntu @ Apolo: ~/hp_threads] $ gcc -lpthread main.c && ./a.out r1 r2 r1 r2 r1 r2 r1 r2 s1 s2 ^ C – RishiD

4

Está haciendo un mal uso de las condiciones. No entendí exactamente lo que quiere, pero lo que está tratando de hacer se parece mucho a un problema típico de sincronización llamado problema productor/consumidor que puede implementarse con condiciones como lo hice después.

Debe echar un vistazo a esto para comprender cómo se usan las condiciones. El problema es el siguiente: Tengo dos hilos, uno que escribe datos en un búfer de tamaño fijo y otro lee datos del búfer. El primer subproceso no puede escribir más datos si el búfer está lleno, el segundo no puede leer si el búfer está vacío.

#include <stdlib.h> 
#include <stdio.h> 

#include <pthread.h> 

#define BUFFER_SIZE 4 

int buffer_nb_entries = 0; 

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 

void send(){ 
for(;;){ 
    pthread_mutex_lock(&mutex); 
    while(buffer_nb_entries == BUFFER_SIZE) /* If buffer is full, then wait */ 
    pthread_cond_wait(&cond, &mutex); 

    /* Here I am sure that buffer is not full */ 
    printf("sending\n"); 
    buffer_nb_entries++; 

    pthread_cond_signal(&cond); // signal that the condition has changed. 
    pthread_mutex_unlock(&mutex); 
} 
} 

void receive(){ 
    for(;;){ 
    pthread_mutex_lock(&mutex); 
    while(buffer_nb_entries == 0) 
     pthread_cond_wait(&cond, &mutex); 
    /* Here I am sure that buffer is not empty */ 
    printf("receiving\n"); 
    buffer_nb_entries--; 
    pthread_cond_signal(&cond); 
    pthread_mutex_unlock(&mutex); 
    } 

} 

int main(){ 
    pthread_t s, r; 

    pthread_create(&s, NULL, (void *(*)(void*))send, NULL); 
    pthread_create(&r, NULL, (void *(*)(void*))receive, NULL); 

    pthread_join(s, NULL); 

} 

Observe que debe probar algo antes de llamar pthread_cond_wait(), si no lo hace y si la señal Función ha sido llamado antes, entonces puede dormir para siempre.

Cuestiones relacionadas