Search
Cílem cvičení je seznámení se s vlákny, jejich implementací a vytvořením vícevláknové aplikace. Použití vláken je založeno na knihovně pthread (POSIX threads) a příklad demonstrujte použití základních konstrukcí pthread knihovny.
pthread
Zdrojové kódy a šablona pro cvičení Prezentace pro cvičení
Vlákna majá datový typ pthread a vytvoří se zavoláním funkce pthread_create. Každé vlákno má svůj úkol, tento úkol je zabalem do funkcí (např. routine), které vlákno svým vytvořenám inkovuje. Vlákna mohou mít sdílená data, které jsou do výpočtu (funkce) předávána pomocí argumentů funkce. Vlákna se korektně ukončují pomocí pthread_join.
routine
void* routine(void* args); ... pthread_t thread; pthread_create(&thread, NULL, routine, ¶maters); pthread_join(thread, 0);
(void *print_thread (void *arg))
counter
(void* compute_thread(void* arg))
pthread_create()
compute_thread
print_thread
V případě, kdy je nutné přistupovat k proměnné sdílené více vlákny je zpravidla žádoucí zabranit souběhu definováním kritické sekce zámkem (mutexem), který dovolí vstup do sekce pouze jedinému vláknu. Kritická sekce může být např. implementována s využitím zámku pthread_mutex_t.
pthread_mutex_t
Mutex se vytváří jako globální proměnná, která se inicializuje funkcí pthread_mutex_init() s výchozími atributy.
pthread_mutex_init()
pthread_mutex_t mtx; pthread_mutex_init(&mtx, NULL);
Inicializaci mutexu se provádí před prvním použitím v příslušném vlákně. Je důležité si uvědomit, že vlákno může být spuštěno dříve, než je očekáváno (V podstatě spíše počítejte s tím, že vlákno je spuštěno ihned po vytvoření.)
Přístupy ke globální proměnnám (counter) se dávají do kritické sekce použitím pthead_mutex_lock() a pthread_mutex_unlock().
pthead_mutex_lock()
pthread_mutex_unlock()
void *routine(void *arg) { ... pthread_mutex_lock(&mtx); counter += 1; pthread_mutex_unlock(&mtx); ... }
mtx
Synchronizaci vláken se může implementovat mechanismem podmíněných proměnných (conditional variable), tj. proměnná typu pthread_cond_t, kterou použijeme k pozastavení vykonávání vlákna voláním pthread_cond_wait. Takto čekající vlákno můžeme následně probudit voláním pthread_cond_signal nebo pthread_cond_broadcast. Používá se pokud práce vlákna závisí na změně v globální proměnné, např. aktualizace výpisů globální proměnné. (V některých případech může být práce vlákna velmi náročná a neefektivní. Například při aktualizace výpisu v cyklu output_thread je výpočetně velmi náročná, neboť cyklus je opakován tak rychle jak dovolují prostředky CPU. V našem případě je taková extermní frekvence aktualizace zbytečná a neefektivní (např. při běhu notebooku/tabletu na baterii se zbytečně čerpá omezená energie a navíc zařízení zahříváme).)
pthread_cond_t
output_thread
Deklaruje se globální proměnná condvar pro podmíněnou proměnnou a inicializujte se výchozími parametry.
condvar
pthread_cond_t condvar; ... pthread_cond_init(&condvar, NULL);
pthread_cond_wait()
void *update_thread(void*)
void *output_thread(void*)
update_thread
output_thread()
void* output_thread(void *d) { ... pthread_mutex_lock(&mtx); ... pthread_cond_wait(&condvar, &mtx); ... pthread_mutex_unlock(&mtx); ...
pthread_cond_signal()
void* update_thread(void *d) { ... pthread_mutex_lock(&mtx); counter += 1; ... pthread_cond_signal(&condvar); ... pthread_mutex_unlock(&mtx); ...