Una variable de condición es una primitiva de sincronización que bloquea uno o más hilos hasta que se recibe una señal de otro hilo sobre el cumplimiento de alguna condición o hasta que ha transcurrido el tiempo máximo de espera. Las variables de condición se utilizan junto con un mutex asociado y son una característica de algunos tipos de monitores .
Conceptualmente, una variable de condición es una cola de subprocesos asociados con un objeto de datos compartidos que esperan que se imponga alguna condición en el estado de los datos. Así, cada variable de condición está asociada a una sentencia . Cuando un subproceso está esperando una variable de condición, no se considera que posee los datos, y otro subproceso puede modificar el objeto compartido y señalar a los subprocesos en espera si la afirmación tiene éxito .
Este ejemplo ilustra el uso de variables de condición para sincronizar subprocesos de productor y consumidor. El subproceso productor, aumentando gradualmente el valor de la variable compartida, le indica al subproceso que espera en la variable de condición que se cumple la condición de valor máximo excedido. Un subproceso de consumidor en espera, que verifica el valor de una variable compartida, se bloquea si no se cumple la condición máxima. Cuando se indica que la afirmación es verdadera, el subproceso "consume" el recurso compartido, disminuyendo el valor de la variable compartida para que no caiga por debajo del mínimo permitido.
En la biblioteca POSIX Threads para C, las funciones y las estructuras de datos con el prefijo pthread_cond son responsables de usar variables de condición.
Código fuente en C usando hilos POSIX #incluir <stdlib.h> #incluir <stdio.h> #include <unistd.h> #incluir <pthread.h> #define STORAGE_MIN 10 #define STORAGE_MAX 20 /* Recurso compartido */ almacenamiento int = ALMACENAMIENTO_MIN ; pthread_mutex_t exclusión mutua ; pthread_cond_t condición ; /* Función de subproceso del consumidor */ vacío * consumidor ( vacío * argumentos ) { puts ( "[CONSUMIDOR] subproceso iniciado" ); int para consumir = 0 ; mientras ( 1 ) { pthread_mutex_lock ( & exclusión mutua ); /* Si el valor de la variable compartida es menor que el máximo, * entonces el subproceso entra en el estado de espera de una señal de que * se ha alcanzado el máximo */ while ( almacenamiento < STORAGE_MAX ) { pthread_cond_wait ( & condición , & mutex ); } aConsumir = almacenamiento - ALMACENAMIENTO_MIN ; printf ( "[CONSUMIDOR] almacenamiento es máximo, consumiendo %d \n " , \ toConsume ); /* "Consumo" del volumen permitido a partir del valor de la * variable compartida */ almacenamiento -= paraConsumir ; printf ( "[CONSUMIDOR] almacenamiento = %d \n " , almacenamiento ); pthread_mutex_unlock ( & mutex ); } devuelve NULL ; } /* Función de subproceso productor */ vacío * productor ( vacío * argumentos ) { puts ( "[PRODUCTOR] subproceso iniciado" ); mientras ( 1 ) { Usleep ( 200000 ); pthread_mutex_lock ( & exclusión mutua ); /* El productor incrementa constantemente el valor de la variable compartida */ ++ almacenamiento ; printf ( "[PRODUCTOR] almacenamiento = %d \n " , almacenamiento ); /* Si el valor de la variable compartida alcanza o excede * el máximo, se notifica al subproceso del consumidor */ si ( almacenamiento >= STORAGE_MAX ) { puts ( "[PRODUCTOR] almacenamiento máximo" ); pthread_cond_signal ( & condición ); } pthread_mutex_unlock ( & mutex ); } devuelve NULL ; } int principal ( int argc , char * argv []) { int res = 0 ; pthread_t thProducer , thConsumer ; pthread_mutex_init ( & mutex , NULL ); pthread_cond_init ( & condición , NULL ); res = pthread_create ( & thProducer , NULL , productor , NULL ); si ( res != 0 ) { perror ( "pthread_create" ); salir ( EXIT_FAILURE ); } res = pthread_create ( & thConsumer , NULL , consumidor , NULL ); si ( res != 0 ) { perror ( "pthread_create" ); salir ( EXIT_FAILURE ); } pthread_join ( thProducer , NULL ); pthread_join ( thConsumer , NULL ); devuelve SALIR_ÉXITO ; }El estándar C ++ 11 agregó soporte para subprocesos múltiples al lenguaje. El trabajo con variables condicionales se proporciona mediante los medios declarados en el archivo de encabezado condition_variable
Texto fuente en C++ (C++11) #incluye <cstdlib> #incluir <iostream> #incluir <subproceso> #incluir <mutex> #include <variable_condición> #incluir <crono> #define STORAGE_MIN 10 #define STORAGE_MAX 20 almacenamiento int = ALMACENAMIENTO_MIN ; std :: mutex mutex global ; std :: condition_variable condition ; /* Función de subproceso del consumidor */ consumidor vacío () { std :: cout << "[CONSUMIDOR] subproceso iniciado" << std :: endl ; int para consumir = 0 ; mientras ( verdadero ) { std :: unique_lock < std :: mutex > lock ( globalMutex ); /* Si el valor de la variable compartida es menor que el máximo, * entonces el subproceso entra en el estado de espera de una señal de que * se ha alcanzado el máximo */ si ( almacenamiento < STORAGE_MAX ) { condición _ esperar ( bloquear , []{ devolver almacenamiento >= STORAGE_MAX ;} ); // Atómicamente _libera el mutex_ e inmediatamente bloquea el subproceso para Consumir = almacenamiento - ALMACENAMIENTO_MIN ; std :: cout << "[CONSUMIDOR] el almacenamiento es máximo, consumiendo" << a Consumir << std :: endl ; } /* "Consumo" del volumen permitido a partir del valor de la * variable compartida */ almacenamiento -= paraConsumir ; std :: cout << "[CONSUMIDOR] almacenamiento = " << almacenamiento << std :: endl ; } } /* Función de subproceso productor */ productor vacío () { std :: cout << "[PRODUCTOR] subproceso iniciado" << std :: endl ; mientras ( verdadero ) { std :: this_thread :: sleep_for ( std :: crono :: milisegundos ( 200 )); std :: unique_lock < std :: mutex > lock ( globalMutex ); ++ almacenamiento ; std :: cout << "[PRODUCTOR] almacenamiento = " << almacenamiento << std :: endl ; /* Si el valor de la variable compartida alcanza o excede * el máximo, se notifica al subproceso del consumidor */ si ( almacenamiento >= STORAGE_MAX ) { std :: cout << "[PRODUCTOR] almacenamiento máximo" << std :: endl ; condición _ notificar_uno (); } } } int principal ( int argc , char * argv []) { std :: subproceso thProducer ( productor ); std :: subproceso thConsumer ( consumidor ); thProductor . unirse (); Consumidor . unirse (); devolver 0 ; }cw.h
#ifndef CW_H #define CW_H #incluir <QSubproceso> #incluir <QMutex> #include <QWaitCondition> #incluir <QDebug> #define STORAGE_MIN 10 #define STORAGE_MAX 20 almacenamiento interno externo ; externo QMutex qmt ; condición QWaitCondition externa ; Productor de clase : QThread público { Q_OBJETO privado : ejecución nula () { qDebug () << "[PRODUCTOR] subproceso iniciado" ; mientras ( 1 ) { QThread :: msleep ( 200 ); qmt . bloquear (); ++ almacenamiento ; qDebug () << "[PRODUCTOR] almacenamiento = " << almacenamiento ; /* Si el valor de la variable compartida alcanza o excede * el máximo, se notifica al subproceso del consumidor */ si ( almacenamiento >= STORAGE_MAX ) { qDebug () << "[PRODUCTOR] almacenamiento máximo" ; condición _ despertarUno (); } qmt . desbloquear (); } } }; Consumidor de clase : QThread público { Q_OBJETO privado : ejecución nula () { qDebug () << "[CONSUMIDOR] subproceso iniciado" ; int para consumir = 0 ; mientras ( 1 ) { qmt . bloquear (); /* Si el valor de la variable compartida es menor que el máximo, * entonces el subproceso entra en el estado de espera de una señal de que * se ha alcanzado el máximo */ si ( almacenamiento < STORAGE_MAX ) { condición _ esperar ( & qmt ); aConsumir = almacenamiento - ALMACENAMIENTO_MIN ; qDebug () << "El almacenamiento de [CONSUMIDOR] es máximo, consumiendo" << a Consumir ; } /* "Consumo" del volumen permitido a partir del valor de la * variable compartida */ almacenamiento -= paraConsumir ; qDebug () << "[CONSUMIDOR] almacenamiento = " << almacenamiento ; qmt . desbloquear (); } } }; #endif /* CW_H */principal.cpp
#include <Aplicación QCore> #incluir "cw.h" almacenamiento int = ALMACENAMIENTO_MIN ; QMutex qmt ; QWaitCondition condición ; int principal ( int argc , char * argv []) { aplicación QCoreApplication ( argc , argv ); Productor producto ; contras del consumidor ; producto _ inicio (); contras _ inicio (); aplicación de devolución . ejecutivo (); }En Python , las variables de condición se implementan como instancias de la clase de Conditionmódulo threading. El siguiente ejemplo usa la misma variable de condición en los hilos productor y consumidor usando la sintaxis del administrador de contexto [1]
# Un subproceso de consumidor con cond_var : # en el contexto de una condición de cond_var mientras que an_item_is_disponible (): # mientras que el artículo no está disponible cond_var . esperar () # esperar obtener_un_elemento () # obtener el elemento # Subproceso productor con cond_var : # en el contexto de una condición cond_var make_an_item_disponible () # producir un elemento cond_var . notificar () # notificar a los consumidoresEn el lenguaje Ada , no hay necesidad de usar variables de condición. Es posible utilizar tipos de datos protegidos para organizar monitores de bloqueo de tareas.
Código fuente de Ada '95 con Ada.Text_IO ; procedimiento principal es Productor de tareas ; -- productor tarea declaración tarea Consumidor ; -- declaración de tarea del consumidor tipo Storage_T es rango 10 .. 20 ; -- tipo de rango para compartir -- monitor (objeto protegido) compartido por productor y consumidor tipo protegido El almacenamiento es la entrada Put ; -- operación "producir" entrada de unidad de recursos Obtener ; -- operación para "consumir" la cantidad permitida de valor de entrada de recursos ( val : out Storage_T ) ; -- descriptor de acceso de valor variable privado -- variable oculta con valor inicial mínimo del rango de tipo StorageData : Storage_T := Storage_T ' First ; fin de almacenamiento ; -- implementación del monitor Cuerpo protegido de almacenamiento El almacenamiento es la entrada Put when StorageData < Storage_T ' Last is begin StorageData := StorageData + 1 ; si StorageData >= Storage_T ' Last entonces Ada . texto_IO . Put_Line ( "[PRODUCTOR] almacenamiento máximo" ); terminar si ; fin ; entrada Obtener cuando StorageData >= Storage_T ' Last is To_Consume : Storage_T ; begin To_Consume := StorageData - Storage_T ' First ; StorageData := StorageData - To_Consume ; ada _ texto_IO . Put_Line ( "[CONSUMIDOR] consumiendo" ); fin Obtener ; valor de entrada ( val : out Storage_T ) cuando verdadero es begin val := StorageData ; fin ; fin de almacenamiento ; -- instancia de monitor Storage Storage1 : Storage ; -- implementación del cuerpo de la tarea de la tarea del productor Producer is v : Storage_T ; comienza Ada . texto_IO . Put_Line ( "[PRODUCTOR] Tarea iniciada" ); retardo de bucle 0.2 ; Almacenamiento1 . poner ; Almacenamiento1 . Valor ( v ); ada _ texto_IO . put ( "[PRODUCTOR]" ); ada _ texto_IO . Poner_Línea ( v ' Img ); bucle final ; Productor final ; -- cuerpo de la tarea de implementación de la tarea del consumidor Consumidor es comenzar Ada . texto_IO . Put_Line ( "[CONSUMIDOR] Tarea iniciada" ); almacenamiento de bucle1 . _ obtener ; bucle final ; Consumidor final ; comenzar nulo ; finPrincipal ; _