Buscar..


Introducción

Las variables condicionales son útiles en los casos en que desea que un subproceso espere algo que sucede en otro subproceso. Por ejemplo, en un escenario productor / consumidor con uno o más subprocesos productores y un subproceso consumidor, se pueden usar variables condicionales para indicar al subproceso consumidor que hay nuevos datos disponibles.

Observaciones

Proceso general

Una espera en una variable condicional (queueCond en el ejemplo productor / consumidor) siempre está acoplada a un mutex (el queueMutex en el ejemplo productor / consumidor), y siempre debería estar acoplada a una variable de estado "normal" también (queue.empty ( ) en el ejemplo productor / consumidor). Cuando se usa correctamente, esto asegura que no se pierdan datos nuevos en el consumidor.

En general, el proceso debe ser:

  • Para el hilo de señalización:
    1. Bloquear el mutex
    2. Actualizar cualquier datos y variables de estado
    3. Señala la variable de condición
    4. Desbloquear el mutex
  • Para el hilo de espera:
    1. Bloquear el mutex
    2. Hacer un while bucle en la variable de estado, de enlace, siempre y cuando los datos no está listo
    3. En el while de bucle, hacer una espera en la variable de condición con pthread_cond_wait()
    4. Cuando los while las salidas de bucle, ahora estamos seguros de que nuevos datos está listo, y que el mutex está bloqueado
    5. Hacer algo con los datos
    6. Desbloquea el mutex y repite.

Con este esquema, sin importar cuándo se programen los subprocesos de señalización y espera, el subproceso en espera nunca perderá datos (como en, nunca se quedará atascado esperando para siempre con los datos válidos listos). Esto se puede realizar al intentar ejecutar manualmente los pasos para el subproceso de señalización, registrando los estados del mutex, la condición y las variables de estado, para cada uno de los pasos en el subproceso en espera.

pthread_cond_wait y el mutex

Para facilitar el proceso anterior, es necesario llamar a pthread_cond_wait() con el mutex bloqueado. Cuando se le llama, pthread_cond_wait() luego desbloqueará la exclusión mutua antes de poner el hilo en suspensión y, justo antes de regresar por cualquier motivo, la exclusión mutua se volverá a bloquear. Esto también significa que si algún otro subproceso tiene el mutex actualmente bloqueado, pthread_cond_wait() esperará a que el mutex se desbloquee, y hasta que el subproceso en espera adquiera el mutex, contenderá con otros hilos que intenten bloquear El mutex al mismo tiempo.

Despertares espurios

También, puede parecer como si el while bucle de espera en la variable de estado podría ser sustituido por un simple if declaración. Sin embargo, el while que se necesita bucle, como el estándar POSIX permite pthread_cond_wait() para hacer los llamados "falsos" activaciones durante la espera, sin llegar a ser señalado. Por lo tanto, el código debe volver a verificar la variable de estado para ver si pthread_cond_wait() devolvió debido a que realmente fue señalado, o debido a una de estas activaciones falsas.

Ejemplo productor / consumidor

pthread_mutex_t queueMutex;
pthread_cond_t queueCond;
Queue queue;

void Initialize() {
    //Initialize the mutex and the condition variable
    pthread_mutex_init(&queueMutex, NULL);
    pthread_cond_init(&queueCond, NULL);
}

void Producer() {
    //First we get some new data
    Data *newData = MakeNewData();

    //Lock the queue mutex to make sure that adding data to the queue happens correctly
    pthread_mutex_lock(&queueMutex);

    //Push new data to the queue
    queue.push(newData);

    //Signal the condition variable that new data is available in the queue
    pthread_cond_signal(&queueCond);

    //Done, unlock the mutex
    pthread_mutex_unlock(&queueMutex);
}


void Consumer() {

    //Run the consumer loop
    while(1) {

        //Start by locking the queue mutex
        pthread_mutex_lock(&queueMutex);

        //As long as the queue is empty,
        while(queue.empty()) {
            // - wait for the condition variable to be signalled
            //Note: This call unlocks the mutex when called and
            //relocks it before returning!
            pthread_cond_wait(&queueCond, &queueMutex);
        }

        //As we returned from the call, there must be new data in the queue - get it,
        Data *newData = queue.front();
        // - and remove it from the queue
        queue.pop();

        //Now unlock the mutex
        pthread_mutex_unlock(&queueMutex);
        
        // - and process the new data
        ProcessData(newData);
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow