{{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.