Vlákna
Vlákna, podobně jako procesy, umožňují aplikaci realizovat více činností najednou. Podobně jako procesy se vlákna jeví jako běžící současně. Jádro systému plánuje jejich činnost asynchronně – vlákna přerušuje, aby přidělilo čas procesoru jiným vláknům.
Koncepčně je vlákno řešeno tak, že běží uvnitř procesu. Vlákno vlastně představuje realizaci procesu na „jemnější“ úrovni. Když spustíte nějaký program, Linux vytvoří nový proces a v tomto procesu vytvoří vlákno. Toto vlákno může vytvářet další vlákna – všechna tato vlákna realizují tentýž program a běží v tomtéž procesu. Přitom každé vlákno může realizovat jinou úlohu programu, a to v kterémkoliv daném čase.
Všechna vlákna v procesu sdílí tutéž paměť, tytéž deskriptory a ostatní systémové zdroje. Pokud nějaké vlákno změní hodnotu určité proměné, bude tato proměnná modifikována pro všechna vlákna v procesu. To samé platí pro deskriptory souborů, pokud jedno vlákno uzavře deskriptor souboru, ostatní vlákna nemohou dále provádět operace s tímto souborem. Protože všechna vlákna běží v rámci jediného procesu, platí, že pokud jedno vlákno zavolá funkci exec, všechna ostatní vlákna se ukončí, protože proces začne realizovat jiný kód (ale jiný proces může samozřejmě vytvářet nová vlákna).
Operační systém Linux implentuje vlákna odpovídající standardu POSIX. Všechny funkce a konstanty pro práci s vlákny jsou definovány v hlavičkovém souboru pthread.h. Tyto funkce nejsou součástí standardní knihovy C, proto musíte při sestavování programu zadat gcc volbu -lpthread.
Vytváření vláken
Každé vlákno je v procesu určeno svým identifikačním číslem. Pokud budete používat proměnnou pro ukládání tohoto čísla, deklarujte ji pomocí typu pthread_t.
V okamžiku vytvoření realizuje každé vlákno speciální funkci vlákna (thread function). Tato funkce je pouze obyčejná funkce obsahující kód, který má vlákno realizovat jako první. Když se funkce vrátí, vlákno bylo vytvořeno. Funkce vlákna má jediný parametr typu void * a jejím návratovým typem je opět void *. Parametr funkce se nazývá argument vlákna a systém předává tento parametr, aniž by jej ověřoval. Parametr lze využít k předávání dat do vlákna nebo k přenosu dat z vlákna do volajícího kódu.
Nové vlákno vytvoříte pomocí funkce pthread_create(). Funkci se předávají tyto argumenty:
- Ukazatel na proměnnou typu pthread_t, do které se uloží identifikační číslo nově vytvořeného vlákna.
- Ukazatel na objekt atribut vlákna (thread attribute). Tento objekt obsahuje detailní informace o tom, jak má vlákno komunikovat se zbývajícími komponentami programu. Pokud prostřednictvím tohoto parametru předáte NULL, budou nastaveny implicitní atributy. Podrobnosti uvedu v některém z příštích dílů.
-
Ukazatel na funkci vlákna. Jedná se o obyčejný ukazatel na funkci typu:
void *(*) (void *)
- Argument vlákna pro přenos dat typu void *. Pomocí tohoto argumentu můžete předávat funkci jakákoliv data.
Volání funkce pthread_create() se vrací okamžitě a původní vlákno pokračuje další instrukcí za tímto voláním. Mezitím nové vlákno začíná realizovat kód, který je obsažen ve funkci vlákna. Systém plánuje vlákna asynchronně, a proto se nemůžete spoléhat na relativní pořadí, ve kterém jsou instrukce prováděny ve dvou či více vláknech.
Program v příkladu vytvoří nové vlákno, které nepřetržitě tiskne do stderr písmena x. Po zavolání funkce pthread_create() tiskne hlavní vlákno opakovaně písmena o do stderr.
#include <pthread.h> #include <stdio.h> /* Funkce vlakna - tiskne pismena x do stderr. Parametr neni pouzivan. Funkce se nevraci. */ void *print_x(void *unused) { while(1) fputc('x', stderr); return(NULL); } int main() { pthread_t thread_id; /* Vytvori nove vlakno. Nove vlakno bude vykonavat funkci print_x(). */ pthread_create(&thread_id, NULL, &print_x, NULL); /* Tiskne pismena o do stderr. */ while(1) fputc('o', stderr); return(0); }
Pokud program přeložíte (nesmíte zapomenout na volbu -lpthread) a spustíte, začnou se na obrazovce střídavě zobrazovat skupiny písmen x a o, a to zcela nepravidelně. Je tomu tak proto, že Linux plánuje přidělování času vláknům asynchronně.
Za normálních okolností se vlánko ukončuje jedním ze dvou způsobů. Prvním je návrat z funkce vlákna, návratová hodnota funkce se považuje za návratovou hodnotu vlákna. Ten byl demonstrován v příkladu (i když nebude nikdy použit). Druhý způsob, jak vlákno ukončit, představuje volání funkce pthread_exit(). Tato funkce může být volána z funkce vlákna nebo z některé jiné funkce (přímo nebo nepřímo). Jediným argumentem funkce je návratový kód vlákna.
Myslím, že pro dnešek toho bylo až až. V příštím dílu se podíváme na předávání dat do vlákna, spojování vláken a povíme si něco o návratových kódech vláken.