Saltar al contenido

¿Pthread_cond_wait (& cond_t, & mutex); desbloquear y luego bloquear el mutex?

Nuestro equipo de trabajo ha estado largas horas investigando para darle solución a tus interrogantes, te regalamos la respuestas y nuestro deseo es serte de mucha ayuda.

Solución:

Existen muchos texto sobre el tema de las variables de condición y su uso, así que no lo aburriré con un montón de detalles desagradables. La razón por la que existen es para permitirle notificar el cambio en un predicado estado. Los siguientes son crítico en la comprensión del uso adecuado de las variables de condición y su asociación mutex:

  • pthread_cond_wait() simultaneamente desbloquea el mutex y comienza a esperar a que se señale la variable de condición. así que debes siempre tener la propiedad del mutex antes de invocarlo.

  • pthread_cond_wait() regresa con el mutex bloqueado, por lo tanto, debe desbloquear el mutex para permitir su uso en otro lugar cuando termine con él. Si la devolución ocurrió porque la variable de condición fue señalada o no no es relevante. Aún debe verificar su predicado independientemente para tener en cuenta el potencial despertares espurios.

  • El propósito del mutex es no para proteger la variable de condición; es para proteger el predicado en el que la variable de condición se utiliza como mecanismo de señalización. Este es, sin lugar a dudas, el idioma más incomprendido de las variables de condición pthread y sus exclusiones mutuas. La variable de condición no necesita protección de exclusión mutua; los datos del predicado lo hace. Piense en el predicado como un estado externo que está siendo monitoreado por los usuarios del par condición-variable / mutex.

Por ejemplo, un trivial pero obviamente incorrecto fragmento de código para esperar una bandera booleana fSet:

bool fSet = false;

int WaitForTrue()

    while (!fSet)
    
        sleep(n);
    

Debería ser obvio que el problema principal es el predicado, fSet, no está protegido en absoluto. Muchos las cosas pueden salir mal aquí. Ej .: Desde el momento en que evalúa la condición while hasta el momento en que comienza a esperar (o girar, o lo que sea), el valor puede haber cambiado. Si esa notificación de cambio es de alguna manera omitido, estás esperando innecesariamente.

Podemos cambiar esto un poco para que al menos el predicado esté protegido de alguna manera. Exclusión mutua en ambos modificadores y la evaluación del predicado se proporciona fácilmente con (qué más) un mutex.

pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
bool fSet = false;

int WaitForTrue()

    pthread_mutex_lock(&mtx);
    while (!fSet)
        sleep(n);
    pthread_mutex_unlock(&mtx);

Bueno, eso parece bastante simple. Ahora nunca evaluamos el predicado sin primero obtener acceso exclusivo a él (bloqueando el mutex). Pero este sigue siendo un problema importante. Bloqueamos el mutex, pero nunca lo soltamos hasta que nuestro ciclo haya terminado. Si todos los demás siguen las reglas y esperan el bloqueo mutex antes de la evaluación o modificación de fSet, nunca podrán hacerlo hasta que renunciemos al mutex. El único “alguien” que puede hacer eso en este caso es nosotros.

Entonces, ¿qué hay de agregar aún más capas a esto? esto funcionara?

pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
bool fSet = false;

int WaitForTrue()

    pthread_mutex_lock(&mtx);
    while (!fSet)
    
        pthread_mutex_unlock(&mtx);
        // XXXXX
        sleep(n);
        // YYYYY
        pthread_mutex_lock(&mtx);
    
    pthread_mutex_unlock(&mtx);

Bueno, sí, “funcionará”, pero aún no es mucho mejor. El período entre XXXXX y YYYYY no somos dueños del mutex (lo cual está bien, ya que no estamos verificando ni modificando fSet de todas formas). Pero en cualquier momento durante ese período, algún otro hilo puede (a) obtener el mutex, (b) modificar fSety (c) libere el mutex, y no sabremos nada hasta que terminemos nuestro sleep(), una vez más obtenga el bloqueo mutex y realice un bucle para otra verificación.

Allí tiene para ser una mejor manera. De alguna manera debería haber una forma de que podamos liberar el mutex y comenzar a esperar algún tipo de señal que nos diga que puede haber ocurrido un cambio en el predicado. Igualmente importante, cuando recibimos esa señal y regresamos a nuestro código, ya deberíamos poseer el bloqueo que nos otorga acceso para verificar los datos del predicado. Este es exactamente lo que una variable de condición está diseñada para proporcionar.


La variable de condición en acción

Ingrese el par condición-variable + mutex. El mutex protege el acceso a cambiar o comprobando el predicado, mientras que la variable condición establece un sistema de seguimiento de un cambio y, lo que es más importante, al hacerlo atómicamente (en lo que a usted respecta, de todos modos) con el predicado exclusión mutua:

int WaitForPredicate()

    // lock mutex (means:lock access to the predicate)
    pthread_mutex_lock(&mtx);

    // we can safely check this, since no one else should be 
    // changing it unless they have the mutex, which they don't
    // because we just locked it.
    while (!predicate)
    
        // predicate not met, so begin waiting for notification
        // it has been changed *and* release access to change it
        // to anyone wanting to by unlatching the mutex, doing
        // both (start waiting and unlatching) atomically
        pthread_cond_wait(&cv,&mtx);

        // upon arriving here, the above returns with the mutex
        // latched (we own it). The predicate *may* be true, and
        // we'll be looping around to see if it is, but we can
        // safely do so because we own the mutex coming out of
        // the cv-wait call. 
    

    // we still own the mutex here. further, we have assessed the 
    //  predicate is true (thus how we broke the loop).

    // take whatever action needed. 

    // You *must* release the mutex before we leave. Remember, we
    //  still own it even after the code above.
    pthread_mutex_unlock(&mtx);

Para que algún otro hilo señale el bucle anterior, hay varias formas de hacerlo, las dos más populares a continuación:

pthread_mutex_lock(&mtx);
TODO: change predicate state here as needed.
pthread_mutex_unlock(&mtx);
pthread_cond_signal(&cv);

De otra manera…

pthread_mutex_lock(&mtx);
TODO: change predicate state here as needed.
pthread_cond_signal(&cv);
pthread_mutex_unlock(&mtx);

Cada uno tiene un comportamiento intrínseco diferente y los invito a hacer algunos deberes sobre esas diferencias y determinar cuál es más apropiado para circunstancias específicas. El primero proporciona un mejor flujo del programa a expensas de introducir potencialmente despertares injustificados. Esto último reduce esos despertares, pero al precio de una menor sinergia de contexto. Cualquiera funcionará en nuestra muestra, y puede experimentar cómo cada uno afecta sus bucles de espera. Independientemente, una cosa es primordial y ambos Los métodos cumplen este mandato:

Nunca cambies, ni chequear, la condición de predicado a menos que el mutex esté bloqueado. Siempre.


Hilo de monitor simple

Este tipo de operación es común en un monitor hilo que actúa en una condición de predicado específica, que (comprobación de errores de sans) generalmente se ve así:

void* monitor_proc(void *pv)

    // acquire mutex ownership
    //  (which means we own change-control to the predicate)
    pthread_mutex_lock(&mtx);

    // heading into monitor loop, we own the predicate mutex
    while (true)
    
        // safe to check; we own the mutex
        while (!predicate)
            pthread_cond_wait(&cv, &mtx);

        // TODO: the cv has been signalled. our predicate data should include
        //  data to signal a break-state to exit this loop and finish the proc,
        //  as well as data that we may check for other processing.
    

    // we still own the mutex. remember to release it on exit
    pthread_mutex_unlock(&mtx);
    return pv;


Un hilo de monitorización más complejo

Modificar este formulario básico para dar cuenta de un notificación El sistema que no requiere que mantenga bloqueado el mutex una vez que haya recibido la notificación se vuelve un poco más complicado, pero no mucho. A continuación se muestra un proceso de monitorización que no mantiene el mutex bloqueado durante el procesamiento regular una vez que hemos establecido que nos han servido (por así decirlo).

void* monitor_proc(void *pv)

    // acquire mutex ownership
    //  (which means we own change-control to the predicate)
    pthread_mutex_lock(&mtx);

    // heading into monitor loop, we own the predicate mutex
    while (true)
    
        // check predicate
        while (!predicate)
            pthread_cond_wait(&cv, &mtx);

        // some state that is part of the predicate to 
        // inform us we're finished
        if (break-state)
            break;

        // TODO: perform latch-required work here.

        // unlatch the mutex to do our predicate-independant work.
        pthread_mutex_unlock(&mtx);

        // TODO: perform no-latch-required work here.

        // re-latch mutex prior to heading into wait
        pthread_mutex_lock(&mtx);            
    

    // we still own the mutex. remember to release it on exit
    pthread_mutex_unlock(&mtx);
    return pv;

¿Dónde usaría alguien algo como ese ? Bueno, suponga que su “predicado” es el “estado” de una cola de trabajo, así como una bandera que le diga que detenga el bucle y salga. Al recibir la notificación de que algo es “diferente”, verifica si debe continuar ejecutando su ciclo, y decide que debe continuar, saque algunos datos de la cola. La modificación de la cola requiere que el mutex esté bloqueado (recuerde, su “estado” es parte de nuestro predicado). Una vez que hemos extraído nuestros datos, los tenemos localmente y podemos procesarlos. independiente del estado de la cola, por lo que liberamos el mutex, hacemos lo nuestro y luego requerimos el mutex para la siguiente vuelta. Hay muchas formas de codificar el concepto anterior, incluido el uso juicioso de pthread_cond_broadcast, etc. Pero se espera que la forma básica sea comprensible.

Esto resultó ser considerablemente más largo de lo que esperaba, pero es un importante obstáculo para las personas que aprenden la programación de pthread, y creo que vale la pena el tiempo / esfuerzo adicional. Espero que hayas sacado algo de eso.

Cuando el primer hilo llama pthread_cond_wait(&cond_t, &mutex); libera el mutex y espera hasta que se condicione cond_t se señala como completo ymutex está disponible.

Así que cuando pthread_cond_signal se llama en el otro hilo, no “despierta” el hilo que espera todavía. mutex debe desbloquearse primero, solo entonces existe la posibilidad de que el primer hilo se bloquee, lo que significa que “al regreso exitoso de pthread_cond_wait mutex se habrá bloqueado y será propiedad del subproceso que realiza la llamada “.

sí, se desbloquea, espera a que se cumpla la condición y luego espera hasta que pueda volver a adquirir el mutex pasado.

Si te apasiona la programación, eres capaz de dejar una reseña acerca de qué te ha gustado de este artículo.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *