2012-06-12 17 views
5

Para comprender el código de las variables de condición pthread, he escrito mi propia versión. ¿Se ve correcto? Lo estoy usando en un programa, está funcionando, pero funciona sorprendentemente mucho más rápido. Originalmente el programa toma alrededor de 2.5 segundos y con mi versión de las variables de condición solo toma 0.8 segundos, y la salida del programa también es correcta. Sin embargo, no estoy seguro, si mi implementación es correcta.Implementación de las variables de condición

struct cond_node_t 
{ 
    sem_t s; 
    cond_node_t * next; 
}; 

struct cond_t 
{ 
    cond_node_t * q;    // Linked List 
    pthread_mutex_t qm;     // Lock for the Linked List 
}; 

int my_pthread_cond_init(cond_t * cond) 
{ 
    cond->q = NULL; 
    pthread_mutex_init(&(cond->qm), NULL); 
} 

int my_pthread_cond_wait(cond_t* cond, pthread_mutex_t* mutex) 
{ 
    cond_node_t * self; 

    pthread_mutex_lock(&(cond->qm)); 
    self = (cond_node_t*)calloc(1, sizeof(cond_node_t)); 
    self->next = cond->q; 
    cond->q = self; 
    sem_init(&self->s, 0, 0); 
    pthread_mutex_unlock(&(cond->qm)); 

    pthread_mutex_unlock(mutex); 
    sem_wait(&self->s); 
    free(self); // Free the node 
    pthread_mutex_lock(mutex); 
} 

int my_pthread_cond_signal(cond_t * cond) 
{ 
    pthread_mutex_lock(&(cond->qm)); 
    if (cond->q != NULL) 
    { 
     sem_post(&(cond->q->s)); 
     cond->q = cond->q->next; 
    } 
    pthread_mutex_unlock(&(cond->qm)); 
} 

int my_pthread_cond_broadcast(cond_t * cond) 
{ 
    pthread_mutex_lock(&(cond->qm)); 
    while (cond->q != NULL) 
    { 
     sem_post(&(cond->q->s)); 
     cond->q = cond->q->next; 
    } 
    pthread_mutex_unlock(&(cond->qm)); 
} 
+0

Está liberando el nodo 'self' sin eliminarlo de la lista. –

+0

@ n.m. El nodo 'self' es sacado por' signal' y 'broadcast'. –

+0

@JensGustedt sí, mi mala –

Respuesta

1

Básicamente su estrategia se ve bien, pero tiene un gran peligro, un comportamiento indefinido, y una viajera recogida:

  • su no están inspeccionando los valores de retorno de sus funciones POSIX. En particular, sem_wait es interrumpible, por lo que bajo carga pesada o mala suerte, su hilo se despertará de manera espuria. Tendría que atrapar cuidadosamente todo lo que
  • ninguna de sus funciones devuelve un valor. Si algún usuario de las funciones decidirá usar los valores de retorno algún día, este es un comportamiento indefinido. Analice cuidadosamente los códigos de error que pueden devolver las funciones de condición y haga exactamente eso.
  • , que proyecten el regreso de malloc o calloc

Editar: En realidad, no es necesario malloc/free en absoluto. Una variable local también lo haría.

+0

¿Por qué no lanzar el retorno de malloc/calloc? – pythonic

+1

Primero, lo más importante, es inútil. Es válido asignar 'void *' a cualquier puntero, esto es para lo que está hecho en C. En segundo lugar, solo use moldes si son absolutamente necesarios. Son difíciles de encontrar textualmente y apagan todas las advertencias y diagnósticos. En tercer lugar, esto puede ocultar un error sutil, cuando olvida el '# include' y el compilador toma esto para devolver' int'. –

2

Usted no parece respetar este requisito:

Estas funciones atómicamente liberar exclusión mutua y hacen que el subproceso de llamada para bloquear la condición cond variables; Atómicamente aquí significa "atómicamente con respecto al acceso por otro hilo al mutex y luego a la variable de condición". Es decir, si otro subproceso es capaz de adquirir el mutex después de que el subproceso about-to-block lo haya liberado, una llamada posterior a pthread_cond_broadcast() o pthread_cond_signal() en ese subproceso se se comportará como si hubiera sido emitido después del el subproceso about-to-block ha bloqueado.

Desbloquea y espera. Otro hilo puede hacer muchas cosas entre estas operaciones.

P.S. No estoy seguro si interpreto este párrafo correctamente, siéntase libre de señalar mi error.

+0

No veo que esto sea un problema aquí. El semáforo se inicializó correctamente. Entonces, incluso si otro hilo entra en acción, el semáforo guardará cualquier token que será 'posteado' por 'señal' o 'emisión'. Tal operación 'post' puede ocurrir antes de que el hilo realmente llame a' sem_wait' o mientras ya está adentro. En ambos casos, el hilo continuará la ejecución. –

4

Además de las comprobaciones de valor de retorno que faltan, hay algunas cuestiones que deberían ser más corregible:

  • sem_destroy no se llama.
  • Señal/transmisión toque el cond_node_t después de activar el hilo de destino, lo que podría resultar en un uso después de libre.

Otros comentarios:

  • El omitido destruyen operación puede requerir cambios en las otras operaciones de lo que es seguro para destruir la variable condición cuando POSIX dice que será seguro. No apoyar destruir o imponer restricciones más fuertes sobre cuándo se llamará simplificará las cosas.
  • Una implementación de producción manejaría la cancelación de subprocesos.
  • Hacer una copia de seguridad de una espera (como la necesaria para la cancelación de subprocesos y los tiempos de espera pthread_cond_timedwait) puede provocar complicaciones.
  • Su implementación pone en cola los hilos en el territorio del usuario, lo que se hace en algunas implementaciones de producción por motivos de rendimiento; No entiendo exactamente por qué.
  • Su implementación siempre pone en cola los hilos en orden LIFO. Esto a menudo es más rápido (como debido a los efectos de caché) pero puede llevar a la inanición. La implementación de producción puede usar FIFO a veces para evitar la inanición.
Cuestiones relacionadas