2. diel - Vlákna - Príklady viacvláknových aplikácií vo VB.NET
V dnešnom VB.NET tutoriále si vyskúšame naprogramovať dve viacvláknové aplikácie, v ktorých sa naučíme vlákna zakladať, pomenovať a spúšťať.
Tlač jednotiek a nul
V našej prvej viacvláknovej aplikácii si ukážeme paralelný beh
dvoch vlákien na striedavej tlači hodnôt 1
a
0
do konzoly:
Konzolová aplikácia
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ...
Založme si novú konzolovú aplikáciu s názvom
TiskJednicek_A_Nul
.
Metóda TiskniJednicky()
Do triedy Program
si napíšme metódu pre tlač hodnoty
1
v nekonečnom cykle while
:
Sub TiskniJednicky() While True Console.Write("1 ") End While End Sub
Metóda TiskNula()
Podobne si pridajme metódu TiskNula()
, ktorá bude v
nekonečnom cykle tlačiť hodnotu 0
:
Sub TiskNula() While True Console.Write("0 ") End While End Sub
Metóda Main()
Nakoniec obe metódy zavolajme v metóde Main()
:
Module Module1 Sub Main() TiskniJednicky() TiskNula() End Sub ... End Module
Testovanie
Keď aplikáciu spustíme, vidíme neustálu tlač čísla
1
:
Konzolová aplikácia
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ...
Na tlač čísla 0
sa samozrejme nedostalo, pretože sme zostali
zacyklení v metóde TiskniJednicky()
. Poďme to napraviť
pridaním ďalšieho vlákna.
Úprava metódy Main()
V metóde Main()
vytvoríme nové vlákno pre tlač nuly pomocou
triedy Thread
:
Imports System.Threading Module Module1 Sub Main() Dim vlaknoNula As New Thread(AddressOf TiskNula) vlaknoNula.Start() TiskniJednicky() End Sub ... End Module
Najskôr vytvoríme nové vlákno vlaknoNula
, v ktorého
konštruktore zavoláme našu metódu TiskNula()
. Vlákno
vlaknoNula
potom spustíme. Nakoniec zavoláme metódu
TiskniJednicky()
, ktorá bude v nekonečnom cykle tlačiť hodnotu
1
do konzoly.
Testovanie
Po spustení aplikácie teraz v konzole vidíme striedanie tlače čísel
1
a 0
, čo dokazuje paralelný beh oboch vlákien:
Konzolová aplikácia
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ...
Poradie tlače 1
a 0
sa bude líšiť
pri každom spustení aplikácie.
V ukážke vidíme, ako operačný systém poskytuje plánovač vlákien. Plánovač vlákien rozhoduje o tom, ktoré vlákno má právo vykonať svoj kód. Plánovač môže prideľovať časové kvantum každému vláknu. Keď jedno vlákno dosiahne koniec svojho časového kvanta, plánovač prepne na iné vlákno.
Úlohy
Predstavme si aplikáciu, ktorá spracováva úlohy v aplikácii. Založme si
konzolovú aplikáciu s názvom Ukoly
. V našej aplikácii
bude:
- Prvé vlákno zodpovedné za načítanie úloh ("Načítacie vlákno").
- Druhé vlákno bude vykonávať načítané úlohy ("Spracovávacie vlákno").
Konzolová aplikácia
[Načítací vlákno pro úkoly] Načítání nových úkolů...
[Zpracovávací vlákno pro úkoly] Zpracovávání úkolů...
[Načítací vlákno pro úkoly] Nové úkoly načteny.
[Načítací vlákno pro úkoly] Přiřazování úkolu: Úkol 1: Implementace nového rozhraní.
[Zpracovávací vlákno pro úkoly] Zpracovávání úkolu 1.
[Načítací vlákno pro úkoly] Přiřazování úkolu: Úkol 2: Testování aplikace.
[Načítací vlákno pro úkoly] Přiřazování úkolu: Úkol 3: Oprava chyb v kódu.
[Načítací vlákno pro úkoly] Všechny úkoly přiřazeny.
[Zpracovávací vlákno pro úkoly] Zpracovávání úkolu 2.
[Zpracovávací vlákno pro úkoly] Zpracovávání úkolu 3.
[Zpracovávací vlákno pro úkoly] Všechny úkoly zpracovány.
Budeme pracovať iba v module Module1.vb
.
Pole assignedTasks
Nad metódu Main()
deklarujeme pole
assignedTasks()
:
Dim assignedTasks() = {"Úkol 1: Implementace nového rozhraní", "Úkol 2: Testování aplikace", "Úkol 3: Oprava chyb v kódu"}
Metóda NactiUkoly()
V tejto metóde implementujeme načítanie nových úloh:
Sub NactiUkoly() Dim threadName As String = Thread.CurrentThread.Name Console.WriteLine($"[{threadName}] Načítanie nových úloh...") Thread.Sleep(2000) Console.WriteLine($"[{threadName}] Nové úlohy načítané.") For Each task As String In assignedTasks Console.WriteLine($"[{threadName}] Priraďovanie úlohy: {task}") Thread.Sleep(1000) Next Console.WriteLine($"[{threadName}] Všetky úlohy priradené") End Sub
Najprv si uložíme názov vlákna do premennej threadName
pomocou vlastnosti Thread.CurrentThread.Name
. Potom vytlačíme
názov vlákna do konzoly spolu s informáciami o priebehu načítavania a
priraďovania úloh.
Metóda ZpracujUkoly()
V tejto metóde budeme načítané úlohy spracovávať:
Sub ZpracujUkoly() Dim threadName As String = Thread.CurrentThread.Name Console.WriteLine($"[{threadName}] Spracovávanie úloh:") For i As Integer = 1 To assignedTasks.Length Console.WriteLine($"[{threadName}] Spracovávanie úlohy {i}") Thread.Sleep(2000) Next Console.WriteLine($"[{threadName}] Všetky úlohy spracované") End Sub
Najprv si uložíme názov vlákna do premennej threadName
pomocou vlastnosti Thread.CurrentThread.Name
. Potom vytlačíme
názov vlákna do konzoly spolu s informáciami o priebehu spracovania
úloh.
Metóda Main()
A nakoniec v metóde Main()
obe vlákna spustíme:
Imports System.Threading
Module Module1
Dim assignedTasks() = {"Úloha 1 Implementácia nového rozhrania", "Úloha 2: Testovanie aplikácie", "Úloha 3: Oprava chýb v kóde"}
Sub Main()
Dim nacitaciVlakno As New Thread(AddressOf NactiUkoly)
nacitaciVlakno.Name = "Načítacie vlákno pre úlohy"
nacitaciVlakno.Start()
Dim zpracovavaciVlakno As New Thread(AddressOf ZpracujUkoly)
zpracovavaciVlakno.Name = "Spracovávacie vlákno pre úlohy"
zpracovavaciVlakno.Start()
Console.ReadKey()
End Sub
Sub NactiUkoly()
Dim threadName As String = Thread.CurrentThread.Name
Console.WriteLine($"[{threadName}] Načítanie nových úloh...")
Thread.Sleep(2000)
Console.WriteLine($"[{threadName}] Nové úlohy načítané.")
For Each task As String In assignedTasks
Console.WriteLine($"[{threadName}] Priraďovanie úlohy: {task}")
Thread.Sleep(1000)
Next
Console.WriteLine($"[{threadName}] Všetky úlohy priradené")
End Sub
Sub ZpracujUkoly()
Dim threadName As String = Thread.CurrentThread.Name
Console.WriteLine($"[{threadName}] Spracovávanie úloh:")
For i As Integer = 1 To assignedTasks.Length
Console.WriteLine($"[{threadName}] Spracovávanie úlohy {i}")
Thread.Sleep(2000)
Next
Console.WriteLine($"[{threadName}] Všetky úlohy spracované")
End Sub
End Module
V metóde vytvoríme vlákna nacitaciVlakno
a
zpracovavaciVlakno
. Do vlastnosti Name
vložíme ich
názvy. A potom už len metódou Start()
obe vlákna spustíme.
Testovanie
Po spustení aplikácie uvidíme výpis práce oboch vlákien:
Konzolová aplikácia
[Načítací vlákno pro úkoly] Načítání nových úkolů...
[Zpracovávací vlákno pro úkoly] Zpracovávání úkolů
[Načítací vlákno pro úkoly] Nové úkoly načteny
[Načítací vlákno pro úkoly] Přiřazování úkolu: Úkol 1: Implementace nového rozhraní
[Zpracovávací vlákno pro úkoly] Zpracovávání úkolu 1
[Načítací vlákno pro úkoly] Přiřazování úkolu: Úkol 2: Testování aplikace
[Načítací vlákno pro úkoly] Přiřazování úkolu: Úkol 3: Oprava chyb v kódu
[Načítací vlákno pro úkoly] Všechny úkoly přiřazeny
[Zpracovávací vlákno pro úkoly] Zpracovávání úkolu 2
[Zpracovávací vlákno pro úkoly] Zpracovávání úkolu 3
[Zpracovávací vlákno pro úkoly] Všechny úkoly zpracovány
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.