Lekce 1 - Úvod do vícevláknových aplikací v VB.NET
Vítejte u prvního dílu seriálu o programování vícevláknových aplikací neboli multithreadingu v VB .NET. Naučíme se zde plně využívat moderní vícejádrové procesory a také spouštět na pozadí úlohy, které by jinak zasekly hlavní vlákno aplikace. Dostaneme se i k nejnovějším technologiím z .NET frameworku jako jsou paralelní programování, úlohy nebo klíčová slova async a await.
Úvod do teorie vláken
Než začneme používat vlákna, popišme si jakým způsobem spouští aplikace náš operační systém. Jelikož jsme u VB, tak můžeme hovořit o Windows, avšak princip platí pro většinu ostatních. Windows je víceúlohový operační systém (multitasking). To znamená, že dokáže spustit více aplikací najednou. Přitom jádro procesoru dokáže obvykle spustit jen jednu instrukci v jeden čas. Jistě víte, že Windows dokázal spouštět více aplikací najednou dlouho předtím, než byly na trhu vícejádrové procesory a i teď si můžete spustit kolik aplikací chcete a to bez ohledu na to, kolikajádrový procesor vlastníte. Jak je to možné? Windows mezi spuštěnými aplikacemi jednoduše rychle přepíná (přesněji to dělá tzv. scheduler a o přepínání hovoříme jako o timeslicingu). Systém jednu aplikaci zastaví a spustí na okamžik aplikaci jinou. Uživatel to vnímá jako by aplikace běžely najednou, i když tomu tak ve skutečnosti není. S příchodem vícejádrových procesorů se princip nezměnil, Windows stále přepíná mezi aplikacemi, ovšem dokáže vykonávat několik instrukcí najednou na každém procesorovém jádru.
Aplikace, proces a vlákno
Zamysleme se nad termíny aplikace, proces a vlákno. Aplikaci si jistě dokážeme představit, je to např. internetový prohlížeč, ve kterém čtete tento článek. Co je ovšem proces a co vlákno?
Proces
Proces je instance běžící aplikace. Pokud si spustíme 3x kalkulačku, nalezneme ve správci úloh 3x proces calc.exe. Některé složitější aplikace mohou využívat několik procesů, např. Google Chrome vytvoří proces pro každou otevřenou záložku. Většinou mají aplikace však jen jeden proces.

Vlákno
V procesu může běžet několik jeho vláken. Každá aplikace má minimálně jeden proces, ve kterém běží její hlavní vlákno, případně nějaká další vlákna. Zatím, co hlavní vlákno máme připravené, další vlákna si vytváříme my sami. Operační systém spustí naše vlákno na nějakém jádru a potom ho rychle uspává a probouzí jak se mu to zrovna hodí. Ve výsledku nám vlákna v procesu běží paralelně a můžeme např. provádět nějaké složité analýzy, aniž bychom zasekli hlavní vlákno aplikace a to dokonce i v několika vláknech najednou, přičemž bude samotný výpočet několikrát rychlejší.
Synchronizace
Vše zní skvěle, že? Vlákna se ovšem v praxi používají opravdu jen pokud je nutně potřebujeme. Je s nimi totiž spojený jeden obrovský problém a tím je synchronizace. Nikdy nevíme, kdy budou naše vlákna uspána, systém to provede bez ohledu na to, co dané vlákno právě dělá. Asi si dokážeme představit, že se metoda vlákna zasekne na nějakém řádku zdrojového kódu. Ve skutečnosti se však může zaseknout i např. v polovině sčítání, protože na 32bitovém procesoru jsou ke sčítání 64bitových čísel zapotřebí 2 instrukce. Pokud tuto hodnotu používáme jiným vláknem, může v ní být nesmysl. Proměnné se také v jádrech cachují, takže se může stát, že má ve stejný čas stejná proměnná několik různých hodnot. K problémům synchronizace se dostaneme během seriálu poměrně podrobně a naučíme se je také řešit.
Vlákna určitě nejsou něco, co by každá aplikace musela nutně obsahovat a ačkoli se inženýři z Microsoftu snaží co mohou, aby práci s nimi co nejvíce zjednodušili (a že se jim to daří), stále technologie ve výsledku aplikaci zkomplikuje. Určitě je používejte s rozvahou.
První vícevláknová aplikace
Naprogramujeme si první vícevláknovou aplikaci. Pro zjednodušení budeme nějakou dobu pracovat pouze v konzoli. Založte si nový projekt, který pojmenujte Prepinac. K projektu přidáme stejnojmennou třídu s následujícím obsahem:
Imports System.Threading Public Class Prepinac Public Sub Vypisuj0() While True Console.Write("0") End While End Sub Public Sub Vypisuj1() While True Console.Write("1") End While End Sub Public Sub Prepinej() Dim vlakno As Thread = New Thread(AddressOf Vypisuj0) vlakno.Start() Vypisuj1() End Sub End Class
První 2 metody třídy jsou velmi jednoduché a simulují nějakou delší činnost. První metoda do nekonečna vypisuje nuly do konzole, druhá metoda stejným způsobem vypisuje jedničky.
Metoda Prepinej() je pro nás již zajímavější. Sama spustí výpis jedniček, ale ještě předtím vytvoří nové vlákno, kterému přiřadí metodu pro výpis nul. Toto vlákno poté také spustí.
Vlákna jsou v .NETu reprezentována třídou Thread. V konstruktoru ji předáme delegát ThreadStart, který má následující podobu:
Public Delegate Sub ThreadStart
Vláknu tedy můžeme předat bezparametrickou metodu typu void. V metodě main vytvoříme instanci přepínače a necháme ho přepínat:
Sub Main() Dim prepinac As Prepinac = New Prepinac() Prepinac.Prepinej() End Sub
Když aplikaci spustíme, získáme takovýto výstup:

Všimněte si, že jedničky a nuly nejsou úplně na střídačku, jak bychom mohli očekávat. Je vidět, jak vlákno chvíli běží a poté je uspáno. Intervaly se také liší, i když průměrně běží obě vlákna stejně dlouho.
Vlastnosti třídy Thread
Zatím pro nás budou důležité pouze tyto vlastnosti na třídě Thread:
- IsAlive - Označuje, zda metoda vlákna běží.
- Name - Každé vlákno si můžeme pojmenovat, což nám usnadní ladění aplikace
Hlavní vlákno aplikace
K hlavnímu vláknu aplikace se dostaneme pomocí statické vlastnosti Thread.CurrentThread. Bohužel nemá ve výchozím stavu přiřazeno žádné jméno, zkusme mu ho přiřadit a vypsat.
Sub Main() Thread.CurrentThread.Name = "Hlavní vlákno" Console.WriteLine(Thread.CurrentThread.Name) End Sub
V příštím dílu, Vlákna v VB.NET - Sleep, Join a lock, se podíváme na uspávání vláken, jejich spojování a základy synchronizace.