1. diel - Úvod do viacvláknových aplikácií v VB.NET
Vitajte u prvého dielu seriálu o programovaní viacvláknových aplikácií čiže multithreadingu v VB .NET. Naučíme sa tu plne využívať moderné viacjadrové procesory a tiež spúšťať na pozadí úlohy, ktoré by inak zasekli hlavné vlákno aplikácie. Dostaneme sa aj k najnovším technológiám z .NET frameworku ako sú paralelné programovanie, úlohy alebo kľúčové slová async a Await.
Úvod do teórie vlákien
Než začneme používať vlákna, popíšme si akým spôsobom spúšťa aplikácie náš operačný systém. Keďže sme u VB, tak môžeme hovoriť o Windows, avšak princíp platí pre väčšinu ostatných. Windows je viacúlohový operačný systém (multitasking). To znamená, že dokáže spustiť viac aplikácií naraz. Pritom jadro procesora dokáže zvyčajne spustiť len jednu inštrukciu v jeden čas. Iste viete, že Windows dokázal spúšťať viacero aplikácií naraz dlho predtým, než boli na trhu viacjadrové procesory a aj teraz si môžete spustiť koľko aplikácií chcete a to bez ohľadu na to, kolikajádrový procesor vlastníte. Ako je to možné? Windows medzi spustenými aplikáciami jednoducho rýchlo prepína (presnejšie to robí tzv. Scheduler ao prepínanie hovoríme ako o timeslicingu). Systém jednu aplikáciu zastaví a spustí na okamih aplikáciu inú. Užívateľ to vníma ako by aplikácia bežali naraz, aj keď to tak v skutočnosti nie je. S príchodom viacjadrových procesorov sa princíp nezmenil, Windows stále prepína medzi aplikáciami, avšak dokáže vykonávať niekoľko inštrukcií naraz na každom procesorové jadrá.
Aplikácie, proces a vlákno
Zamyslime sa nad termíny aplikácie, proces a vlákno. Aplikáciu si iste dokážeme predstaviť, je to napr. Internetový prehliadač, v ktorom čítate tento článok. Čo je však proces a čo vlákno?
Proces
Proces je inštancia bežiace aplikácie. Pokiaľ si spustíme 3x kalkulačku, nájdeme v správcovi úloh 3x proces calc.exe. Niektoré zložitejšie aplikácie môžu využívať niekoľko procesov, napr. Google Chrome vytvorí proces pre každú otvorenú záložku. Väčšinou majú aplikácie však len jeden proces.
Vlákno
V procese môže bežať niekoľko jeho vlákien. Každá aplikácia má minimálne jeden proces, v ktorom beží jej hlavné vlákno, prípadne nejaká ďalšie vlákna. Zatiaľ, čo hlavný vlákno máme pripravené, ďalšie vlákna si vytvárame my sami. Operačný systém spustí naše vlákno na nejakom jadre a potom ho rýchlo uspáva a prebúdza ako sa mu to zrovna hodí. Vo výsledku nám vlákna v procese beží paralelne a môžeme napr. Vykonávať nejaké zložité analýzy, bez toho aby sme zasekli hlavné vlákno aplikácie a to dokonca aj v niekoľkých vláknach naraz, pričom bude samotný výpočet niekoľkokrát rýchlejšie.
Synchronizácia
Všetko znie skvele, že? Vlákna sa však v praxi používajú naozaj len pokiaľ je nutne potrebujeme. Je s nimi totiž spojený jeden obrovský problém a tým je synchronizácia. Nikdy nevieme, kedy budú naše vlákna uspanie, systém to vykoná bez ohľadu na to, čo dané vlákno práve robí. Asi si dokážeme predstaviť, že sa metóda vlákna zasekne na nejakom riadku zdrojového kódu. V skutočnosti sa však môže zaseknúť aj napr. V polovici sčítanie, pretože na 32-bitovom procesore sú ku sčítanie 64bitových čísel potrebné 2 inštrukcie. Ak túto hodnotu používame iným vláknom, môže v nej byť nezmysel. Ukazovatele sa tiež v jadrách cachují, takže sa môže stať, že má v rovnaký čas rovnaká premenná niekoľko rôznych hodnôt. K problémom synchronizácia sa dostaneme počas seriálu pomerne podrobne a naučíme sa je tiež riešiť.
Vlákna určite nie sú niečo, čo by každá aplikácia musela nutne obsahovať a hoci sa inžinieri z Microsoftu snaží čo môžu, aby prácu s nimi čo najviac zjednodušili (a že sa im to darí), stále technológia vo výsledku aplikácii skomplikuje. Určite je používajte s rozvahou.
Prvý viacvláknové aplikácie
Naprogramujeme si prvé viacvláknové aplikácie. Pre zjednodušenie budeme nejakú dobu pracovať iba v konzole. Založte si nový projekt, ktorý pomenujte Prepinac. K projektu pridáme rovnomennou triedu s nasledujúcim obsahom:
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
Prvé 2 metódy triedy sú veľmi jednoduché a simulujú nejakú dlhšiu činnosť. Prvá metóda do nekonečna vypisuje nuly do konzoly, druhá metóda rovnakým spôsobom vypisuje jedničky.
Metóda Prepínajte () je pre nás už zaujímavejšie. Sama spustí výpis jedničiek, ale ešte predtým vytvorí nové vlákno, ktorému priradí metódu pre výpis núl. Toto vlákno potom tiež spustí.
Vlákna sú v .NET reprezentované triedou Thread. V konstruktoru ju odovzdáme delegát ThreadStart, ktorý má túto podobu:
Public Delegate Sub ThreadStart
Vláknu teda môžeme odovzdať bezparametrickou metódu typu void. V metóde main vytvoríme inštanciu prepínače a necháme ho prepínať:
Sub Main() Dim prepinac As Prepinac = New Prepinac() Prepinac.Prepinej() End Sub
Keď aplikáciu spustíme, získame takýto výstup:
Všimnite si, že jednotky a nuly nie sú úplne na striedačku, ako by sme mohli očakávať. Je vidieť, ako vlákno chvíľu beží a potom ich uspanie. Intervaly sa tiež líšia, aj keď priemerne beží obe vlákna rovnako dlho.
Vlastnosti triedy Thread
Zatiaľ pre nás budú dôležité len tieto vlastnosti na triede Thread:
- IsAlive - Označuje, či metóda vlákna beží.
- Name - Každé vlákno si môžeme pomenovať, čo nám uľahčí ladenie aplikácie
Hlavné vlákno aplikácie
K hlavnému vláknu aplikácie sa dostaneme pomocou statické vlastnosti Thread.CurrentThread. Bohužiaľ nemá v predvolenom stave priradené žiadne meno, skúsme mu ho priradiť a vypísať.
Sub Main() Thread.CurrentThread.Name = "Hlavní vlákno" Console.WriteLine(Thread.CurrentThread.Name) End Sub
V budúcom dieli, Vlákna v VB.NET - Sleep, Join a lock , sa pozrieme na uspávanie vlákien, ich spájanie a základy synchronizácie.