{{indexmenu_n>12}} ====== Lab 12 - Vícevláknové aplikace ====== * Pro vyučující: [[courses:bab36prga:internal:tutorialinstruction:12|]] /* * [[https://docs.google.com/presentation/d/1HBgUYFCPfGIcYmHjLGuv74sXHcbvkpbi0NkpThGVVVE/edit?usp=sharing|Prezentace pro cvičení ]] */ {{ :courses:bab36prga:labs:prga-lab12-sources.zip |Výchozí soubory prga-lab12-sources.zip}}\\ {{ :courses:bab36prga:lectures:bab36prga-lec11-codes.zip |}} ===== Procvičovaná téma ===== * Opakování vláken (pthread) * Implemetace vícevláknové aplikace * Implementace vícevláknové aplikace s meziprocesovou komunikací ''MPA''. Cílem cvičení je implementovat program s více paralelně běžícími vlákny z přednášky [[courses:bab36prga:lectures:start#vicevlaknove_programovani_modely_aplikaci_posix_vlakna_c11_vlakna|lec11]] a následně jej rozšířit do programu ovládající jinou aplikaci prostřednictvím komunikace pojmenovanou rourou, viz [[courses:bab36prga:labs:lab10|Lab 10 - Meziprocesová komunikace]]. ===== Implemetace vícevláknové aplikace ===== Můžeme využít výchozí soubor ''main.c'' ve kterém máme připraveny funkce pro tři různá vlákna: - ''input_thread()'' je vlákno zpracování vstupu (čtení stiknuté klávesy); - ''output_thread()'' je vlákno aktualizující výstup (jednořádkový); - ''alarm_thread()'' je vlákno časovače, který po uplynutí definované periody zvýší hodnotu proměnné (čítače). Perioda časovače může být nastavována uživatelem, stiskem definovaných kláves. Použití vláken je založeno na knihovně ''pthread'' (POSIX threads) a příklad demonstruje použití základních konstrukcí ''pthread'' knihovny. Úkol na cvičení je implementace aplikace z přednášky [[courses:bab36prga:lectures:start#vicevlaknove_programovani_modely_aplikaci_posix_vlakna_c11_vlakna|lec11]], kterou lze přímo použít. Nicméně postupná a samostatná implementace umožní pochopit principy. ==== Úkoly ==== * Použijte bloky z minulého cvičení pro vytvoření aplikace se třemi vlákny. Ve skutečnosti bude mít aplikace vlákna 4, včetně hlavního vlákna ''main()'' funkce. * Přidejte jméno (textový řetězec) identifikující každé vlákno: "Input", "Output", and "Alarm". * "Alarm" vlákno inkrementálně zvyšuje hodnotu čítače po uplynutí definové periody. * Použijte pole a datové struktury, které umožní zautomatizovat správu vláken v cyklech, například pole. * Program bude reagovat na následující stisk kláves: * 'q' - ukončí program tak, že všechna vlákna budou korektně ukončena, tj. vyskočí z příslušných cyklech hlavního těla vlákna; * 'r' - sníží periodu pro inkrementaci čítače o 10 ms; * 'p' - zvýší periodu pro inkrementaci čítače o 10 ms. * Minimální hodnota periody je 10 ms. * Maximální hodnota periody je 2000 ms. * Výstup programu je jeden řádek ve tvaru "\rAlarm period: %10i Alarm counter: %10i" Pro čtení kláves bez nutnosti potvrzovat ''Enter'' přepněte terminál do ''raw'' režimu, například tak, jak bylo prezentována v přednášce [[courses:bab36prga:lectures:start#vicevlaknove_programovani_modely_aplikaci_posix_vlakna_c11_vlakna|lec11]]. ++++ Například funkce call_termios()| void call_termios(int reset) { static struct termios tio, tioOld; tcgetattr(STDIN_FILENO, &tio); if (reset) { tcsetattr(STDIN_FILENO, TCSANOW, &tioOld); } else { tioOld = tio; //backup cfmakeraw(&tio); tcsetattr(STDIN_FILENO, TCSANOW, &tio); } } ++++ ++++ Použijte strukturu pro zapouzdření proměnných sdílených jednotlivými vlákny.| typedef struct { int alarm_period; int alarm_counter; bool quit; pthread_mutex_t *mtx; // avoid global variables for mutex and pthread_cond_t *cond; // conditional variable } data_t; // data structure shared among the threads ++++ Použijte kritickou sekci (mutex) pro přístup ke sdíleným datům. ++++ Např. ve výstupním vlákně| pthread_mutex_lock(&mtx); pthread_cond_wait(&cond, &mtx); // wait for next event q = data->quit; printf("\rAlarm time: %10i Alarm counter: %10i", data->alarm_period, data->alarm_counter); fflush(stdout); pthread_mutex_unlock(&mtx); ++++ ++++ Nebo ve vlákně alarm_thread() při počátační incializaci lokálních proměnných | pthread_mutex_lock(&mtx); bool q = data->quit; useconds_t period = data->alarm_period * 1000; // alarm_period is in ms pthread_mutex_unlock(&mtx); ++++ Použijte zasílání signálů pro komunikaci mezi vlákny a zabránění plýtvání výpočetním výkonem. Fakticky je nutné překreslit výstup ve vlákně (funkci) ''output_thread()'' pouze pokud došlo k nějaké změně, uživatelský vstup nebo zvýšení hodnoty čítače. ++++ Například ve vlákně alarm_thread() | while (!q) { usleep(period); // calling usleep out of critical section!!! pthread_mutex_lock(&mtx); q = data->quit; data->alarm_counter += 1; period = data->alarm_period * 1000; // update the period is it has been changed pthread_cond_broadcast(&cond); // inform all waiting threads on cond. pthread_mutex_unlock(&mtx); } ++++ === Tipy === * Jelikož výstupní řádek není zakončen koncem řádku může být nutné explicitně vynutit výstup voláním ''flush'' na ''stdout'' ve výstupním vlákně, např. jako fflush(stdout); * Vytvořte novou globální proměnnou ''quit'' k indikaci, že program bude ukončen jakmile uživatel stiskne klávesu ''Enter'' např. bool quit = false; ... getchar(); pthread_mutex_lock(&mtx); quit = true; pthread_mutex_unlock(&mtx); ... ===== Implemetace multiprocesové vícevláknové aplikace MPA ===== V další části cvičení pokračujte směrem k implementaci vícevláknová aplikace s meziprocesovou komunikací, realizovanou pojmenovanou rourou (vizte [[courses:bab36prga:labs:lab10|Lab 10]]). Program "//rozdělte//" na dva programy. * ''mpa-alrm'' je program realizující vzdálený časovač. * ''mpa-ctrl'' je program ovládající //vzdálený// časovače, od kterého přijímá informace o uplynutí času. Budeme uvažovat následující jednobajtovou komunikaci. * 'i' Může vyslat po startu, žádná reakce není očekávána. (''mpa-alrm'' → ''mpa-ctrl'') * 'e' Spuštění časovače (//enable//). (''mpa-alrm'' ← ''mpa-ctrl'') * 'd' Zastavení časovače (//disable//). (''mpa-alrm'' ← ''mpa-ctrl'') * 'p' Zvýšení periody časovače o 10 ms (''mpa-alrm'' ← ''mpa-ctrl'') * 'r' Snížení periody časovače o 10 ms (''mpa-alrm'' ← ''mpa-ctrl'') * 'x' Indikace uplynutí doby časovače (''mpa-alrm'' → ''mpa-ctrl'') * 'b' Indikace ukončení činnosti, program končí. (''mpa-alrm'' ← ''mpa-ctrl'') Program ''mpa-ctrl'' reaguje je na stisk kláves 'e', 'd', 'p', 'r' definované komunikace. Dále reaguje na * 'q' zasláním 'b' časovači a ukončením své činnosti. Pojmenované roury mohou být například * ''/tmp/mpa-alrm.in'' (komunikace ''mpa-alrm'' ← ''mpa-ctrl'') a * ''/tmp/map-ctrl.in'' ''mpa-alrm'' → ''mpa-ctrl''). ++++Vytvoření pojmenovaných rour| #!/bin/sh in=/tmp/mpa-alrm.in out=/tmp/mpa-ctrl.in rm -rf $in $out mkfifo $in mkfifo $out ++++ V obou programech použijeme neblokující čtení/zápis z/do souboru prostřednictvím knihovny ''prg_io_nonblock.c''. ==== Další úkoly ==== * Využijte tzv. barieru pro synchronizaci vláken po startu (''thread_barrier.c''). * Program ''mpa-ctrl'' rozšiřte o odhad akutálně nastavené period v aplikace ''mpa-alrm''. To lze realizovat výpočtem průměrem za nějaký definovaný interval, např. v samostatném vlákně s periodickým výpočtem za 5 sekund.