O variabilă de condiție este o primitivă de sincronizare care blochează unul sau mai multe fire de execuție până când este primit un semnal de la un alt fir de execuție despre îndeplinirea unei anumite condiții sau până la expirarea perioadei maxime de timeout. Variabilele de condiție sunt utilizate împreună cu un mutex asociat și sunt o caracteristică a unor tipuri de monitoare .
Conceptual, o variabilă de condiție este o coadă de fire asociate cu un obiect de date partajat care așteaptă ca o anumită condiție să fie impusă stării datelor. Astfel, fiecare variabilă de condiție este asociată cu o instrucțiune . Când un fir de execuție așteaptă o variabilă de condiție, nu se consideră că deține datele, iar un alt fir poate modifica obiectul partajat și poate semnala firele de execuție în așteptare dacă afirmația reușește .
Acest exemplu ilustrează utilizarea variabilelor de condiție pentru a sincroniza firele producător și consumator. Firul producător, crescând treptat valoarea variabilei partajate, semnalează firului de execuție care așteaptă variabila condiție că este îndeplinită condiția de depășire a valorii maxime. Un fir de execuție de consum în așteptare, verificând valoarea unei variabile partajate, blochează dacă condiția maximă nu este îndeplinită. Când este semnalat că afirmația este adevărată, thread-ul „consumă” resursa partajată, decrementând valoarea variabilei partajate astfel încât aceasta să nu scadă sub minimul permis.
În biblioteca POSIX Threads pentru C, funcțiile și structurile de date prefixate cu pthread_cond sunt responsabile pentru utilizarea variabilelor de condiție.
Cod sursă în C folosind Threads POSIX #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <pthread.h> #define STORAGE_MIN 10 #define STORAGE_MAX 20 /* Resurse partajate */ int stocare = STORAGE_MIN ; pthread_mutex_t mutex ; starea pthread_cond_t ; /* Funcția firului de consum */ void * consumator ( void * args ) { puts ( "[CONSUMER] a început" ); int la Consum = 0 ; în timp ce ( 1 ) { pthread_mutex_lock ( & mutex ); /* Dacă valoarea variabilei partajate este mai mică decât maximul, * atunci firul intră în starea de așteptare a unui semnal că * maximul a fost atins */ while ( stocare < STORAGE_MAX ) { pthread_cond_wait ( & condiție , & mutex ); } toConsume = stocare - STORAGE_MIN ; printf ( "[CONSUMER] stocarea este maximă, consumând %d \n " , \ toConsume ); /* „Consumul” volumului permis din valoarea variabilei * partajate */ stocare -= toConsume ; printf ( "[CONSUMER] stocare = %d \n " , stocare ); pthread_mutex_unlock ( & mutex ); } returnează NULL ; } /* Funcția firului de producție */ void * producător ( void * args ) { puts ( "[PRODUCER] a început" ); în timp ce ( 1 ) { somn ( 200000 ); pthread_mutex_lock ( & mutex ); /* Producătorul crește constant valoarea variabilei partajate */ ++ depozitare ; printf ( "[PRODUCER] stocare = %d \n " , stocare ); /* Dacă valoarea variabilei partajate atinge sau depășește * maximul, thread-ul consumatorului este notificat */ if ( stocare >= STORAGE_MAX ) { pune ( „[PRODUCER] stocare maximă” ); pthread_cond_signal ( & condiție ); } pthread_mutex_unlock ( & mutex ); } returnează NULL ; } int main ( int argc , char * argv []) { int res = 0 ; pthread_t thProducer , thConsumer ; pthread_mutex_init ( & mutex , NULL ); pthread_cond_init ( & condiție , NULL ); res = pthread_create ( & thProducer , NULL , producător , NULL ); dacă ( res != 0 ) { perror ( "pthread_create" ); ieșire ( EXIT_FAILURE ); } res = pthread_create ( & thConsumer , NULL , consumer , NULL ); dacă ( res != 0 ) { perror ( "pthread_create" ); ieșire ( EXIT_FAILURE ); } pthread_join ( thProducer , NULL ); pthread_join ( thConsumer , NULL ); returnează EXIT_SUCCESS ; }Standardul C++11 a adăugat suport pentru multithreading limbajului. Lucrul cu variabile condiționale este furnizat prin mijloacele declarate în fișierul antet condition_variable
Text sursă în C++ (C++11) #include <cstdlib> #include <iostream> #include <fir> #include <mutex> #include <condition_variable> #include <crono> #define STORAGE_MIN 10 #define STORAGE_MAX 20 int stocare = STORAGE_MIN ; std :: mutex globalMutex ; std :: condition_variable condition ; /* Funcția firului de consum */ consumator nul () { std :: cout << „Fiul [CONSUMATOR] a început” << std :: endl ; int la Consum = 0 ; în timp ce ( adevărat ) { std :: unic_lock < std :: mutex > blocare ( globalMutex ); /* Dacă valoarea variabilei partajate este mai mică decât maximul, * atunci firul intră în starea de așteptare a unui semnal că * maximul a fost atins */ if ( stocare < STORAGE_MAX ) { starea . așteptați ( blocare , []{ return storage >= STORAGE_MAX ;} ); // Atomic _eliberează mutexul_ și blochează imediat firul toConsume = storage - STORAGE_MIN ; std :: cout << „[CONSUMER] stocarea este maximă, consumatoare” << toConsume << std :: endl ; } /* „Consumul” volumului permis din valoarea variabilei * partajate */ stocare -= toConsume ; std :: cout << "[CONSUMER] stocare = " << stocare << std :: endl ; } } /* Funcția firului de producție */ nul producător () { std :: cout << „Firea [PRODUCER] a început” << std :: endl ; în timp ce ( adevărat ) { std :: this_thread :: sleep_for ( std :: chrono :: milisecunde ( 200 )); std :: unic_lock < std :: mutex > blocare ( globalMutex ); ++ depozitare ; std :: cout << "[PRODUCER] stocare = " << stocare << std :: endl ; /* Dacă valoarea variabilei partajate atinge sau depășește * maximul, thread-ul consumatorului este notificat */ if ( stocare >= STORAGE_MAX ) { std :: cout << "[PRODUCĂTOR] stocare maximă" << std :: endl ; starea . notify_one (); } } } int main ( int argc , char * argv []) { std :: thread thProducer ( producator ); std :: thread thConsumer ( consumator ); Producatorul . alăturați-vă (); thConsumator . alăturați-vă (); returnează 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 int stocare ; extern QMutex qmt ; extern QWaitCondition condiție ; clasa Producător : public QThread { Q_OBJECT privat : void run () { qDebug () << „Firul [PRODUCER] a început” ; în timp ce ( 1 ) { QThread :: msleep ( 200 ); qmt . blocare (); ++ depozitare ; qDebug () << "[PRODUCER] stocare = " << stocare ; /* Dacă valoarea variabilei partajate atinge sau depășește * maximul, thread-ul consumatorului este notificat */ if ( stocare >= STORAGE_MAX ) { qDebug () << „[PRODUCER] stocare maximă” ; starea . wakeOne (); } qmt . debloca (); } } }; clasă Consumator : QThread public { Q_OBJECT privat : void run () { qDebug () << „Fiul [CONSUMER] a început” ; int la Consum = 0 ; în timp ce ( 1 ) { qmt . blocare (); /* Dacă valoarea variabilei partajate este mai mică decât maximul, * atunci firul intră în starea de așteptare a unui semnal că * maximul a fost atins */ if ( stocare < STORAGE_MAX ) { starea . așteptați ( & qmt ); toConsume = stocare - STORAGE_MIN ; qDebug () << „Spacarea [CONSUMER] este maximă, consumatoare” << la Consum ; } /* „Consumul” volumului permis din valoarea variabilei * partajate */ stocare -= toConsume ; qDebug () << "[CONSUMER] stocare = " << stocare ; qmt . debloca (); } } }; #endif /* CW_H */principal.cpp
#include <QCoreApplication> #include „cw.h” int stocare = STORAGE_MIN ; QMutex qmt ; QWaitCondition condiție ; int main ( int argc , char * argv []) { Aplicația QCoreApplication ( argc , argv ); Producător produs ; contra consumatorului ; prod . începe (); contra . începe (); returnați aplicația . exec (); }În Python , variabilele de condiție sunt implementate ca instanțe ale clasei de Conditionmodul threading. Următorul exemplu utilizează aceeași variabilă de condiție în firele de execuție producător și consumator folosind sintaxa managerului de context [1]
# Un fir de execuție de consum cu cond_var : # în contextul unei condiții cond_var când nu an_item_is_available (): # în timp ce articolul nu este disponibil cond_var . așteptați () # așteptați get_an_item () # obțineți articolul # Firul de producție cu cond_var : # în contextul unei condiții cond_var make_an_item_available () # produce un articol cond_var . notify () # notifica consumatoriiÎn limbajul Ada , nu este nevoie să folosiți variabile de condiție. Este posibil să utilizați tipuri de date protejate pentru a organiza monitoare de blocare a sarcinilor.
Cod sursă Ada '95 cu Ada.Text_IO ; procedura Principala este Producător de sarcini ; -- sarcina de declarare a sarcinii producătorului Consumator ; -- declarația de sarcină a consumatorului tipul Storage_T este intervalul 10 .. 20 ; -- tipul intervalului pentru distribuire -- monitor (obiect protejat) partajat de producator si consumator tip protejat Stocarea este intrare Put ; -- operațiunea „produce” intrarea unității de resurse Get ; -- operațiune de „consumare” a cantității admisibile de resurse de intrare Value ( val : out Storage_T ); -- Accesorul valorii variabilei private -- variabilă ascunsă cu valoarea iniţială minimă din intervalul de tip StorageData : Storage_T := Storage_T ' First ; Depozitare finală ; -- monitorizează implementarea Corpului protejat de stocare Stocarea este introdusă Put when StorageData < Storage_T ' Last is begin StorageData := StorageData + 1 ; if StorageData >= Storage_T ' Last then Ada . text_IO . Put_Line ( "[PRODUCER] stocare maximă" ); termina daca ; sfârşitul ; intrarea 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 ( "[CONSUMATOR] consumator" ); sfârşitul Get ; Valoare de intrare ( val : out Storage_T ) când true este start val := StorageData ; sfârşitul ; Depozitare finală ; -- monitorizarea instanţei Stocare Stocare1 : Stocare ; -- implementarea sarcinii producătorului organismul de sarcini Producătorul este v : Storage_T ; începe Ada . text_IO . Put_Line ( „[PRODUCER] Sarcina a început” ); întârziere buclă 0,2 ; Depozitare1 . pune ; Depozitare1 . Valoare ( v ); Ada . text_IO . put ( "[PRODUCĂTOR]" ); Ada . text_IO . Put_Line ( v ' Img ); buclă de capăt ; Producator final ; -- organismul de implementare a sarcinii consumatorului Consumatorul începe Ada . text_IO . Put_Line ( „[CONSUMER] Sarcina începută” ); loopStorage1 . _ obține ; buclă de capăt ; Consumatorul final ; începe nul ; endMain ; _