Implementación de variables de condición

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 variables de condición toma solo 0.8 segundos, y la salida del programa también es correcta. Sin embargo, no estoy seguro si mi implementación es correcta.

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));
}

preguntado el 12 de junio de 12 a las 17:06

Estas liberando el self nodo sin eliminarlo de la lista. -

@nm El self el nodo es quitado por signal y broadcast. -

@JensGustedt sí, mi culpa -

Me doy cuenta de que esto es puramente educativo, pero pensé que debería mencionar que los semáforos no necesitan un mutex para garantizar que las señales no se pierdan. Si usted sem_post mientras nadie escucha, sem_wait aún lo atenderá la próxima vez que lo llame. Los semáforos son básicamente contadores atómicos que se bloquean para evitar que se vuelvan negativos. -

Creo que el my_pthread_cond_signal y `my_pthread_cond_broadcast´ tienen la misma implementación, ¿es correcto? -

3 Respuestas

Además de las comprobaciones de valor de retorno que faltan, hay algunos problemas más que deberían solucionarse:

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

Más comentarios:

  • La operación de destrucción omitida puede requerir cambios en las otras operaciones, por lo que es seguro destruir la variable de condición cuando POSIX dice que será seguro. No apoyar la destrucción o imponer restricciones más estrictas sobre cuándo se puede llamar simplificará las cosas.
  • Una implementación de producción manejaría la cancelación de subprocesos.
  • Salir de una espera (como la requerida para la cancelación de subprocesos y pthread_cond_timedwait tiempos de espera) puede dar lugar a complicaciones.
  • Su implementación pone en cola los subprocesos en el espacio 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 subprocesos en orden LIFO. Esto suele ser más rápido (por ejemplo, debido a los efectos de caché), pero puede llevar a la inanición. La implementación de producción puede usar el orden FIFO a veces para evitar el hambre.

Respondido el 13 de junio de 12 a las 00:06

Básicamente, su estrategia se ve bien, pero tiene un gran peligro, un comportamiento indefinido y una pequeña elección:

  • no está inspeccionando los valores de retorno de sus funciones POSIX. En particular sem_wait es interrumpible, por lo que bajo una carga pesada o mala suerte, su hilo se despertará falsamente. Tendrías que atrapar cuidadosamente todo eso
  • ninguna de sus funciones devuelve un valor. Si algún usuario de las funciones decide usar los valores devueltos algún día, este es un comportamiento indefinido. Analice cuidadosamente los códigos de error que las funciones de condición pueden devolver y haga precisamente eso.
  • no eches la vuelta de malloc or calloc

Edit: De hecho, no necesitas malloc/free en absoluto. Una variable local también serviría.

Respondido el 12 de junio de 12 a las 19:06

¿Por qué no emitir el retorno de malloc/calloc? - pitónico

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 olvidas el #include y el compilador toma esto para devolver int. - Jens Gustedt

Parece que no respetas este requisito:

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

Desbloqueas y luego esperas. Otro hilo puede hacer muchas cosas entre estas operaciones.

PD: no estoy seguro de si estoy interpretando este párrafo correctamente, siéntete libre de señalar mi error.

Respondido el 12 de junio de 12 a las 19:06

No veo que esto sea un problema aquí. El semáforo está correctamente inicializado. Entonces, incluso si se activa otro hilo, el semáforo almacenará cualquier token que sea posteditado por un signal or broadcast. Tal post la operación puede ocurrir antes de que el hilo realmente llame sem_wait o mientras ya está dentro. En ambos casos, el subproceso continuará ejecutándose. - Jens Gustedt

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.