Podmínková proměnná je synchronizační primitivum , které blokuje jedno nebo více vláken , dokud není přijat signál z jiného vlákna o splnění nějaké podmínky nebo dokud neuplyne maximální časový limit. Podmínkové proměnné se používají ve spojení s přidruženým mutexem a jsou funkcí některých druhů monitorů .
Koncepčně je proměnná podmínky frontou vláken spojených se sdíleným datovým objektem, která čekají na uložení nějaké podmínky na stav dat. Každá proměnná podmínky je tedy spojena s příkazem . Když vlákno čeká na podmínkovou proměnnou, není považováno za vlastníka dat a jiné vlákno může upravit sdílený objekt a signalizovat čekajícím vláknům, pokud je výraz úspěšný .
Tento příklad ilustruje použití proměnných podmínek k synchronizaci výrobních a spotřebitelských vláken. Produkční vlákno, které postupně zvyšuje hodnotu sdílené proměnné, signalizuje vláknu čekajícímu na podmínku, že je splněna podmínka překročení maximální hodnoty. Čekající spotřebitelské vlákno, které kontroluje hodnotu sdílené proměnné, se zablokuje, pokud není splněna maximální podmínka. Když je signalizováno, že tvrzení je pravdivé, vlákno „spotřebuje“ sdílený prostředek a sníží hodnotu sdílené proměnné tak, aby neklesla pod povolené minimum.
V knihovně POSIX Threads pro C jsou funkce a datové struktury s předponou pthread_cond zodpovědné za použití proměnných podmínek.
Zdrojový kód v C pomocí POSIX Threads #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <pthread.h> #define STORAGE_MIN 10 #define STORAGE_MAX 20 /* Sdílený zdroj */ int úložiště = STORAGE_MIN ; pthread_mutex_t mutex ; pthread_cond_t podmínka ; /* Funkce spotřebitelského vlákna */ void * spotřebitel ( void * argumenty ) { vloží ( "[CONSUMER] vlákno zahájeno" ); int to Consume = 0 ; zatímco ( 1 ) { pthread_mutex_lock ( & mutex ); /* Pokud je hodnota sdílené proměnné menší než maximum, * pak vlákno přejde do stavu čekání na signál, že * bylo dosaženo maxima */ zatímco ( úložiště < STORAGE_MAX ) { pthread_cond_wait ( & condition , & mutex ); } toConsume = úložiště - STORAGE_MIN ; printf ( "[CONSUMER] úložiště je maximální, spotřebovává %d \n " , \ toConsume ); /* "Spotřeba" povoleného objemu z hodnoty sdílené * proměnné */ úložiště -= toConsume ; printf ( "[CONSUMER] storage = %d \n " , storage ); pthread_mutex_unlock ( & mutex ); } vrátit NULL ; } /* Funkce výrobního vlákna */ void * producent ( void * args ) { vloží ( "[PRODUCER] vlákno spuštěno" ); zatímco ( 1 ) { uspání ( 200 000 ); pthread_mutex_lock ( & mutex ); /* Producent neustále zvyšuje hodnotu sdílené proměnné */ ++ úložiště ; printf ( "[PRODUCER] úložiště = %d \n " , úložiště ); /* Pokud hodnota sdílené proměnné dosáhne nebo překročí * maximum, spotřebitelské vlákno je upozorněno */ if ( úložiště >= STORAGE_MAX ) { vloží ( "[PRODUCER] maximum úložiště" ); pthread_cond_signal ( & podmínka ); } pthread_mutex_unlock ( & mutex ); } vrátit NULL ; } int main ( int argc , char * argv []) { int res = 0 ; pthread_t thProducer , thConsumer ; pthread_mutex_init ( & mutex , NULL ); pthread_cond_init ( & podmínka , NULL ); res = pthread_create ( & thProducer , NULL , producent , NULL ); if ( res != 0 ) { chyba ( "pthread_create" ); exit ( EXIT_FAILURE ); } res = pthread_create ( & thConsumer , NULL , spotřebitel , NULL ); if ( res != 0 ) { chyba ( "pthread_create" ); exit ( EXIT_FAILURE ); } pthread_join ( thProducer , NULL ); pthread_join ( thConsumer , NULL ); return EXIT_SUCCESS ; }Standard C++11 přidal do jazyka podporu pro multithreading. Práce s podmíněnými proměnnými je zajištěna prostředky deklarovanými v hlavičkovém souboru condition_variable
Zdrojový text v C++ (C++11) #include <cstdlib> #include <iostream> #include <vlákno> #include <mutex> #include <condition_variable> #include <chrono> #define STORAGE_MIN 10 #define STORAGE_MAX 20 int úložiště = STORAGE_MIN ; std :: mutex globalMutex ; std :: podmínka_proměnná podmínka ; /* Funkce spotřebitelského vlákna */ neplatný spotřebitel () { std :: cout << "[SPOTŘEBITEL] vlákno zahájeno" << std :: endl ; int to Consume = 0 ; zatímco ( pravda ) { std :: unique_lock < std :: mutex > lock ( globalMutex ); /* Pokud je hodnota sdílené proměnné menší než maximum, * pak vlákno přejde do stavu čekání na signál, že * bylo dosaženo maxima */ if ( úložiště < STORAGE_MAX ) { stav . čekat ( lock , []{ vrátit úložiště >= STORAGE_MAX ;} ); // Atomicky _uvolní mutex_ a okamžitě zablokuje vlákno toConsume = storage - STORAGE_MIN ; std :: cout << "[CONSUMER] úložiště je maximální, spotřebovává se" << toConsume << std :: endl ; } /* "Spotřeba" povoleného objemu z hodnoty sdílené * proměnné */ úložiště -= toConsume ; std :: cout << "[CONSUMER] storage = " << storage << std :: endl ; } } /* Funkce výrobního vlákna */ neplatný výrobce () { std :: cout << "Vlákno [PRODUCER] zahájeno" << std :: endl ; zatímco ( pravda ) { std :: toto_vlákno :: sleep_for ( std :: chrono :: milisekundy ( 200 )); std :: unique_lock < std :: mutex > lock ( globalMutex ); ++ úložiště ; std :: cout << "[PRODUCER] storage = " << storage << std :: endl ; /* Pokud hodnota sdílené proměnné dosáhne nebo překročí * maximum, spotřebitelské vlákno je upozorněno */ if ( úložiště >= STORAGE_MAX ) { std :: cout << "Maximální úložiště [PRODUCER]" << std :: endl ; stav . notify_one (); } } } int main ( int argc , char * argv []) { std :: vlákno thProducer ( producent ); std :: vlákno thConsumer ( spotřebitel ); thProducent . připojit se (); thConsumer . připojit se (); návrat 0 ; }cw.h
#ifndef CW_H #define CW_H #include <QThread> #include <QMutex> #include <QWaitCondition> #include <QDebug> #define STORAGE_MIN 10 #define STORAGE_MAX 20 externí vnitřní úložiště ; externí QMutex qmt ; extern QWaitCondition podmínka ; class Výrobce : public QThread { Q_OBJECT soukromý : neplatný běh () { qDebug () << "Vlákno [PRODUCER] spuštěno" ; zatímco ( 1 ) { QVlákno :: msleep ( 200 ); qmt . zámek (); ++ úložiště ; qDebug () << "[PRODUCER] storage = " << storage ; /* Pokud hodnota sdílené proměnné dosáhne nebo překročí * maximum, spotřebitelské vlákno je upozorněno */ if ( úložiště >= STORAGE_MAX ) { qDebug () << "Maximální úložiště [PRODUCER]" ; stav . wakeOne (); } qmt . odemknout (); } } }; třída Consumer : public QThread { Q_OBJECT soukromý : neplatný běh () { qDebug () << "[CONSUMER] vlákno zahájeno" ; int to Consume = 0 ; zatímco ( 1 ) { qmt . zámek (); /* Pokud je hodnota sdílené proměnné menší než maximum, * pak vlákno přejde do stavu čekání na signál, že * bylo dosaženo maxima */ if ( úložiště < STORAGE_MAX ) { stav . čekat ( & qmt ); toConsume = úložiště - STORAGE_MIN ; qDebug () << "Úložiště [CONSUMER] je maximální, spotřeba" << toConsume ; } /* "Spotřeba" povoleného objemu z hodnoty sdílené * proměnné */ úložiště -= toConsume ; qDebug () << "[CONSUMER] storage = " << storage ; qmt . odemknout (); } } }; #endif /* CW_H */main.cpp
#include <QCoreApplication> #include "cw.h" int úložiště = STORAGE_MIN ; QMutex qmt ; QWaitCondition podmínka ; int main ( int argc , char * argv []) { aplikace QCoreApplication ( argc , argv ); Výrobce prod ; spotřebitelské nevýhody ; prod . start (); nevýhody _ start (); návratová aplikace . exec (); }V Pythonu jsou proměnné podmínky implementovány jako instance třídy Conditionmodulu threading. Následující příklad používá stejnou proměnnou podmínky ve vláknech producenta a spotřebitele pomocí syntaxe správce kontextu [1]
# Spotřebitelské vlákno s cond_var : # v kontextu podmínky cond_var, zatímco není an_item_is_available (): # když položka není dostupná cond_var . čekat () # čekat get_an_item () # získat položku # Vlákno producenta s cond_var : # v kontextu podmínky cond_var make_an_item_available () # produkovat položku cond_var . notify () # informovat spotřebiteleV jazyce Ada není potřeba používat podmínkové proměnné. Je možné použít chráněné datové typy k uspořádání monitorů blokujících úlohy.
Zdrojový kód Ada '95 s Ada.Text_IO ; postup Hlavní je výrobce úkolu ; -- úkol prohlášení výrobce úkol Spotřebitel ; -- prohlášení o úkolu spotřebitele typ Storage_T je rozsah 10 .. 20 ; -- typ rozsahu pro sdílení -- monitor (chráněný objekt) sdílený výrobcem a spotřebitelem chráněný typ Storage is entry Put ; -- operace "vyrobit" záznam jednotky zdroje Get ; -- operace pro "spotřebování" povoleného množství položky zdroje Value ( val : out Storage_T ) ; -- hodnota proměnné accessor private -- skrytá proměnná s minimální počáteční hodnotou z rozsahu typu StorageData : Storage_T := Storage_T ' First ; konec Skladování ; -- monitorování implementace Tělo chráněné úložiště Úložiště je záznam Put when StorageData < Storage_T ' Last is begin StorageData := StorageData + 1 ; if StorageData >= Storage_T ' Last then Ada . text_IO . Put_Line ( "Maximální úložiště [PRODUCER]" ); konec if ; konec ; záznam Get when StorageData >= Storage_T ' Last is To_Consume : Storage_T ; begin To_Consume := StorageData - Storage_T ' First ; StorageData := StorageData - To_Consume ; Ada . text_IO . Put_Line ( "[CONSUMER] spotřebovává" ); konec Get ; vstup Hodnota ( hodnota : out Úložiště_T ) , když je true , začíná hodnota := Úložná data ; konec ; konec Skladování ; -- monitorovat instanci Úložiště Úložiště1 : Úložiště ; -- implementace úkolu producenta task body Producer is v : Storage_T ; začít Ada . text_IO . Put_Line ( "[PRODUCER] Úloha zahájena" ); zpoždění smyčky 0,2 ; Skladování1 . dát ; Skladování1 . Hodnota ( v ); Ada . text_IO . vložit ( "[PRODUCER]" ); Ada . text_IO . Put_Line ( v ' Img ); konec -smyčka ; koncový výrobce ; -- spotřebitelský úkol implementace úkolový orgán Consumer is begin Ada . text_IO . Put_Line ( "[CONSUMER] Úloha zahájena" ); loopStorage1 . _ dostat ; konec -smyčka ; konečný spotřebitel ; začít null ; endMain ; _