Lekce 3 - Vlákna - Uspání, blokování a stavy vláken ve VB.NET
V minulé lekci, Vlákna - Příklady vícevláknových aplikací ve VB.NET, jsme si naprogramovali dvě vícevláknové aplikace, ve kterých jsme se naučili vlákna zakládat, pojmenovat a spouštět.
V dnešním VB.NET tutoriálu se naučíme základy
synchronizace vláken. Ukážeme si, jak vlákna
uspat a blokovat. Představíme si třídu
Thread
a seznámíme se se stavy vláken. Vše si
ukážeme na funkčních příkladech.
Uspaní vláken
V rámci práce s vícevláknovým prostředím nabízí třída
Thread
užitečné metody pro řízení toku vláken, mezi nimiž
patří metoda Sleep()
. Metoda Sleep()
na třídě
Thread
se používá pro dočasné pozastavení
běhu aktuálního vlákna na definovanou dobu v
milisekundách. Typickým použitím je simulace čekání na určitou
událost, nebo zpomalení běhu programu.
Pro zápis doby uspání můžeme v parametru metody
Sleep()
využít strukturu TimeSpan
.
Metoda Sleep()
Uspání vlákna na dobu 1 hodiny provedeme takto:
Thread.Sleep(3600000)
Metoda Sleep()
s
TimeSpan
S využitím struktury TimeSpan
uspíme vlákno na stejnou dobu
takto:
Thread.Sleep(TimeSpan.FromHours(1))
Blokování vláken
Další užitečnou metodou třídy Thread
je metoda
Join()
. Metoda Join()
blokuje volající vlákno,
dokud se vlákno reprezentované instancí typu Thread
neukončí.
Metoda umožňuje synchronizaci vláken a čekání na
dokončení určitého vlákna před pokračováním ve zpracování hlavního
vlákna. To je užitečné v situacích, kdy potřebujeme vyčkat na dokončení
nějaké operace.
Příklad
Nejlépe to uvidíme na příkladu. Založme si novou konzolovou aplikaci.
Budeme pracovat pouze v Module1.vb
.
Proměnná hodnota
Nejdříve si nad metodu Main()
vložme proměnou
hodnota
, kterou naplňme hodnotou 0
:
Dim hodnota As Integer = 0
Metoda PrictiJedna()
Pod metodu Main()
si napišme první metodu, kterou budeme
volat, metodu PrictiJedna()
. V metodě implementujeme zvýšení
hodnoty proměnné hodnota
o hodnotu 1
a její výpis
do konzole:
Sub PrictiJedna() For i As Integer = 1 To 10 hodnota += 1 Next Console.WriteLine("Hodnota proměnné: {0}", hodnota) End Sub
Metoda OdectiJedna()
Podobně implementujeme metodu OdectiJedna()
. V metodě tedy
snížíme hodnotu proměnné hodnota
o 1
a vypíšeme
ji do konzole:
Sub OdectiJedna() For i As Integer = 1 To 10 hodnota -= 1 Next Console.WriteLine("Hodnota proměnné: {0}", hodnota) End Sub
Metoda Main()
Pomocné metody implementované máme. Můžeme se tedy vrhnout na
dokončení programu v metodě Main()
:
Imports System.Threading
Module Module1
Dim hodnota As Integer = 0
Sub Main()
Dim pricteniThread As New Thread(AddressOf PrictiJedna)
Dim odecitaniThread As New Thread(AddressOf OdectiJedna)
pricteniThread.Start()
pricteniThread.Join()
odecitaniThread.Start()
odecitaniThread.Join()
Console.WriteLine("Výsledek: {0}", hodnota)
Console.ReadKey()
End Sub
Sub PrictiJedna()
For i As Integer = 1 To 10
hodnota += 1
Next
Console.WriteLine("Hodnota proměnné: {0}", hodnota)
End Sub
Sub OdectiJedna()
For i As Integer = 1 To 10
hodnota -= 1
Next
Console.WriteLine("Hodnota proměnné: {0}", hodnota)
End Sub
End Module
Vytvoříme dvě vlákna. Vlákno:
pricteniThread
, v jehož konstruktoru zavoláme metoduPrictiJedna()
,odecitaniThread
, v jehož konstruktoru zavoláme metoduOdectiJedna()
.
Obě vlákna spustíme a poté na nich zavoláme metodu Join()
.
Nakonec do konzole vytiskneme výslednou hodnotu proměnné
hodnota
.
Testování
Po spuštění aplikace vidíme, jak se hodnota proměnné
hodnota
mění:
Konzolová aplikace
Hodnota proměnné: 10
Hodnota proměnné: 0
Výsledek: 0
Stavy vláken
Podívejme se na některé ze stavů, které vlákno může nabývat. Stav
vlákna lze zjistit pomocí jeho vlastnosti ThreadState
, která je
výčtovým typem sestávajícím z jedné nebo více z
následujících hodnot:
Running
– Vlákno je v běhu, provádí svůj kód.StopRequested
– Bylo požádáno o zastavení vlákna, ale ještě nebylo zastaveno.SuspendRequested
– Bylo požádáno o pozastavení vlákna, ale ještě nebylo pozastaveno.Background
– Vlákno je nastaveno jako pozadí.Unstarted
– Vlákno bylo vytvořeno, ale ještě nebylo spuštěno.Stopped
– Vlákno bylo zastaveno.WaitSleepJoin
– Vlákno čeká v režimu spánku nebo na spojení.Suspended
– Vlákno bylo pozastaveno.AbortRequested
– Bylo požádáno o zrušení vlákna, ale ještě nebylo zrušeno.Aborted
– Vlákno bylo zrušeno.
Příklad
Ukažme si výpis stavů vláken na příkladu.
Metoda Counting()
V konzolové aplikaci si napíšeme tuto metodu:
Sub Counting() For i As Integer = 0 To 4 Console.WriteLine(i) Thread.Sleep(500) Next End Sub
V metodě Counting()
provádíme číslování od 0
do 4
s výpisem čísel. Mezi každý výpis vkládáme pauzu
trvající 500
milisekund.
Metoda Main()
Do metody Main()
přidáme následující kód:
Imports System.Threading
Module Module1
Sub Main()
Dim countingThread As New Thread(AddressOf Counting)
Console.WriteLine("Stav vlákna po vytvoření: {0}", countingThread.ThreadState)
countingThread.Start()
Console.WriteLine("Stav vlákna po spuštění: {0}", countingThread.ThreadState)
Thread.Sleep(100)
Console.WriteLine("Stav vlákna po chvilce čekání: {0}", countingThread.ThreadState)
countingThread.Join()
Console.WriteLine("Stav vlákna po dokončení: {0}", countingThread.ThreadState)
Console.ReadKey()
End Sub
Sub Counting()
For i As Integer = 0 To 4
Console.WriteLine(i)
Thread.Sleep(500)
Next
End Sub
End Module
V metodě Main()
vytváříme nové vlákno pomocí třídy
Thread
a jejího konstruktoru, kterému předáváme metodu
Counting()
. Poté vypisujeme stav vlákna pomocí vlastnosti
ThreadState
v různých bodech životního cyklu vlákna.
Stav vlákna je zajímavý pouze ve scénářích ladění. Kód by nikdy neměl používat stav vlákna k synchronizaci aktivit vláken.
Testování
Spuštěním kódu uvidíme výpis stavů vláken:
Konzolová aplikace
Stav vlákna po vytvoření: Unstarted
Stav vlákna po spuštění: Running
0
Stav vlákna po chvilce čekání: WaitSleepJoin
1
2
3
4
Stav vlákna po dokončení: Stopped
V příští lekci, Vlákna - Bezpečnost vláken ve VB.NET, se budeme zabývat bezpečností vláken ve VB.NET. Vybereme si jednu techniku sdílení dat mezi vlákny, kterou implementujeme do příkladu.