Třetím prostředkem k synchronizaci činnosti vláken v Linuxu je podmíněná proměnná. Pomocí podmíněných proměnných můžete určovat komplexnější podmínky, za kterých budou realizována jednotlivá vlákna.
Předpokládejme, že jste napsali funkci vlákna, která realizuje nekonečný cyklus a při každém průchodu cyklem provede nějakou práci. Takový cyklus ovšem musí být kontrolován nějakým příznakem (stavovou proměnnou). Pokud je příznak nastaven, cyklus se bude provádět, jinak bude ve vyčkávacím stavu.
Příklad ukazuje implementaci takového cyklu. V průběhu každého průchodu cyklem kontroluje funkce vlákna, zda je příznak nastaven. Protože je příznak zpřístupňován více vlákny, je chráněn mutexem. Program však není efektní. Funkce vlákna spotřebuje velké množství času procesoru, kdykoliv příznak není nastaven, protože bude stále dokola ověřovat jeho hodnotu. Efektivnější by bylo uvést vlákno do nečinného stavu, není-li příznak nastaven, a až za jistých okolností by se měl příznak nastavit.
#inclue <pthread.h> int thread_flag; pthread_mutex_t thread_flag_mutex; void initialize_flag() { pthread_mutex_init(&thread_flag_mutex, NULL); thread_flag = 0; } /* Vola opakovane funkci do_work, pokud je nastaven priznak */ void *thread_function(void *thread_arg) { while(1) { int flag_is_set; /* Priznak je chranen zamcenim mutexu */ pthread_mutex_lock(&thread_flag_mutex); flag_is_set = thread_flag; pthread_mutex_unlock(&thread_flag_mutex); if (flag_is_set) do_work(); } return(NULL); } /* Nastavi hodnotu priznaku */ void set_thread_flag(int flag_value) { /* Priznak je chranen zamcenim mutexu */ pthread_meutex_lock(&thread_flag_mutex); thread_flag = flag_value; pthread_mutex_unlock(&thread_flag_mutex); }
Podmíněná proměnná umožňuje implementovat podmínku, za které bude vlákno vykonávat svoji činnost, a nopak, nebude-li podmínka splněna, bude vlákno zablokováno. Pokud bude každé vlákno používající podmíněnou proměnnou měnit smysl podmínky korektně, operační systém Linux garantuje, že vlákna zablokovaná při splnění podmínky budou odblokována, jakmile bude podmínka změněna.
Podobně jako v případě semaforu, vlákno může čekat na podmíněnou proměnnou. Jestliže vlákno A čeká na podmíněnou proměnnou, je zablokováno, dokud jiné vlákno B podmínku nezmění. Jestliže vlákno B změní podmínku předtím, než vlákno A na změnu podmínky začne čekat, signál o změně podmínky je ztracen a vlákno A je zablokováno až do okamžiku, kdy nějaké jiné vlákno podmínku změní.
Nyní si popišme, jak náš problém s podmíněnou proměnou řešit efektivněji.
- Cyklus ve funkci thread_function ověří příznak. Není-li příznak nastaven, vlákno bude čekat na změnu podmíněné proměnné.
- Funkce set_thread_flag signalizuje změnu podmíněné proměnné po změně příznaku. Pokud je funkce thread_function zablokována podmíněnou proměnnou, bude odblokována a bude testovat podmínku znovu.
V uvedeném postupu je však jeden problém. Nastává stav souběhu mezi ověřováním hodnoty příznaku a čekáním na podmíněnou proměnnou. Předpokládejme, že funkce thread_function ověřila příznak a že jej shledala nenastaveným. Dále předpokládejme, že v tomto okamžiku operační systém Linux pozastavil vlákno a spustil hlavní vlákno, a také předpokládejme, že nešťastnou náhodou je hlavní vlákno (vlákno funkce main()) ve funkci set_thread_flag. Tato funkce nastaví příznak a signalizuje změnu podmíněné proměnné. Protože však žádné vlákno nečeká v tomto okamžiku (funkce thread_function byla pozastavena, aby mohla čekat na změnu podmínky) na podmíněnnou proměnnou, signalizace změny podmíněné proměnné se ztratí. Nyní, když operační systém Linux spustí jiné vlákno, začne toto vlákno čekat na podmíněnou proměnnou a může se zablokovat navždy.
Problém lze řešit uzamčením příznaku a podmíněnné proměnné spolu s mutexem. Praktickou ukázku řešení si však necháme na příště – máte se tedy na co těšit.