Vlákna (Thread)
Operační systémy používají pro oddělení různých běžících aplikací procesy. Proces je tvořen paměťovým prostorem a jedním nebo více vlákny. Vlákno je samostatně prováděný výpočetní tok (posloupnost instrukcí), který běží v rámci procesu. Každému vláknu přísluší vlastní priorita a řada systémových struktur.
Operační systémy s preemptivním multitaskingem vytvářejí dojem souběžného provádění více vláken ve více procesech. To je zajištěno rozdělením času procesoru mezi jednotlivá vlákna po malých časových intervalech. Pokud časový interval vyprší, je běžící vlákno pozastaveno, uloží se jeho kontext a obnoví se kontext dalšího vlákna ve frontě, jemuž je pak předáno řízení. Vzhledem k tomu, že tyto časové úseky jsou z pohledu uživatele velmi krátké, je výsledný dojem i na počítači s jediným procesorem takový, jako by pracovalo více vláken současně. V případě, že máme k dispozici více procesorů, jsou mezi ně vlákna přidělována ke zpracování a k současnému běhu pak skutečně dochází.
Přepnutí mezi vlákny bývá výrazně rychlejší než u procesovém multitaskingu, neboť vlákna sdílejí stejnou paměť a uživatelská práva svého mateřského procesu a není je třeba při přepínání měnit. Vlákno také spotřebuje méně paměti a je rychlejší na vytváření.
Vlákna je možné vytvořit i čistě na aplikační úrovni bez nativní podpory operačního systému (využitím sdílené paměti a dalších technik). Takto vzniklá vlákna je poté možné spouštět postupně v jednotlivých procesech operačního systému nebo takzvaně m:n, tedy v několika vláknech operačního systému současně spouštět větší počet aplikačních vláken.
Samotným zvyšováním počtu vláken však obvykle odpovídajícího zvýšení výkonu aplikace nedosáhneme. Naopak se doporučuje, abychom používali co nejméně vláken a tím omezili spotřebu systémových prostředků a nárůst režie. Typické problémy jsou následující:
- Pro ukládání kontextových informací se spotřebovává dodatečná paměť, a tedy celkový počet procesů a vláken, které mohou v systému současně existovat, je omezený.
- Obsluha velkého počtu vláken spotřebovává významnou část času procesoru. Existuje-li tedy příliš mnoho vláken, většina z nich příliš významně nepostupuje. Navíc pokud je většina vláken v jednom procesu, dostávají se vlákna jiných procesů na řadu méně často.
- Organizace programu s mnoha vlákny je složitá a může být zdrojem mnoha chyb. Zejména je obtížné zajistit jejich správnou synchronizaci.
Vlákna v Javě
Každé vlákno v Javě je instancí třídy java.lang.Thread. Tato třída zajišťuje spuštění, zastavení a ukončení vlákna. Vlákno musí implementovat metodu run, která definuje činnost vlákna. Této metodě je předáno řízení po spuštění vlákna metodou start.
Životní cyklus
Vlákno v průběhu svého života prochází posloupností následujících stavů:
- New - bezprostředně po vytvoření ještě nejsou vláknu přiděleny žádné systémové prostředky, vlákno neběží.
- Runnable - po provedené metody start je vlákno připraveno k běhu. V tomto stavu se může nacházet více vláken, ovšem jen jedno z nich (na počítači s jedním procesorem) je ve stavu „běžící“.
- Not runnable - do tohoto stavu se vlákno dostane, je-li pozastaveno voláním jedné s metod sleep, wait nebo suspend, případně čekáním na dokončeníoperace vstupu/výstupu.
- Dead - do tohoto stavu se vlákno dostane ukončením metody run nebo voláním metody stop.
Vícevláknové aplikace a ladění
Hlavní problémy vícevláknových aplikací souvisí se synchronizací
- uváznutí - Deadlock - je situace, kdy úspěšné dokončení první akce je podmíněno předchozím dokončením druhé akce, přičemž druhá akce může být dokončena až po dokončení první akce. Deadlocku můžeme zabránit například tím, že proces musí o všechny prostředky, které potřebuje, zažádat najednou. Buď je všechny dostane, nebo nedostane ani jeden.
- souběh (race conditions) - přístup více vláken ke sdíleným proměnným a alespoň jedno vlákno nevyužívá synchronizačních mechanismů. Vlákno čte hodnotu zatímco jiné vlákno zapisuje. Zápis a čtení nejsou atomické a data mohou být neplatná. K zabránění souběhu se používají zámky (lock), jejichž použitím se zajistí, že jen jedno vlákno může v jeden okamžik přistoupit k označenému resource souboru nebo části kódu.
- vyhladovění - Vyhladovění - stav, kdy jsou vláknu neustále odepírány prostředky. Bez těchto prostředků program nikdy nedokončí svůj úkol.
Příklad: Následující program vytvoří dvě paralelní vlákna, která vždy vypíšou své jméno a pak čekají zadaný počet milisekund.
using System; using System.Threading; public class Vlakno { string jmeno; int interval; public Vlakno(string jmeno, int interval) { this.jmeno = jmeno; this.interval = interval; } public void Run() { while( true ) { Console.WriteLine("Vlakno {0}", jmeno); Thread.Sleep(interval); } } public static void Main() { Vlakno v1 = new Vlakno("v1", 500); Vlakno v2 = new Vlakno("v2", 1000); Thread t1 = new Thread(new ThreadStart(v1.Run)); Thread t2 = new Thread(new ThreadStart(v2.Run)); t1.Start(); t2.Start(); t1.Join(); // Čekat, dokud vlákno 't1' nedokončí práci t2.Join(); } }