Folyamat- és szálkezelés Microsoft Windows 2000/XP/2003/Vista
Definíciók és folyamatmodell Folyamatok és szálak kezelése Ütemezési kérdések Többprocesszoros és többmagos rendszerek
© Miklós Árpád, BMF NIK, 2007
[email protected]
Folyamatmodell (1) Definíciók • Folyamat („process”) – Az operációs rendszerek programokat hajtanak végre • Kötegelt rendszerek: feladatok („job”) • Interaktív rendszerek: felhasználói programok („task”)
– A folyamat egy adott program dinamikus, a virtuális memóriában elhelyezkedő, „élő” példánya) – Fő adminisztrációs egység (állapotinformáció‐tár) • Erőforrások aktuális állapota (memória, fájlok, objektumkezelők, hálózati kapcsolatok...) • Processzoridő‐alapú elszámolást is lehetővé tesz
– Hagyományosan az ütemezés alapegysége • A Windows‐nál a szál („thread”) az ütemezés alapegysége
V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
2
Folyamatmodell (2) Definíciók • Szál („thread”) – A Windows‐nál az ütemezés alapegysége • A processzorokon ténylegesen a szálak futnak, nem a folyamatok
– Folyamaton belüli állapotadminisztrációs egység • Processzorok, ütemezés, I/O műveletek állapotai
– Nincs saját címtere • Az egy folyamathoz tartozó szálak egymással osztoznak a folyamat címterén
– A többszálú működés problémákat vethet fel • Összehangolási pontok (randevú, kilépés) • Adatkezelés – –
V1.0
2008. március 10.
Szálspecifikus adatok kezelése Szál szempontjából globális adatok kezelése
© Miklós Árpád, BMF NIK, 2007
[email protected]
3
Folyamatmodell (3) A Windows‐nál alkalmazott négyszintű modell • 1. szint – feladat („job”) – Folyamatkészlet – Szabályozhatók a benne lévő folyamatok paraméterei • CPU idő, munkakészlet, biztonsági beállítások stb.
– Kötegelt feldolgozáshoz ideális
• 2. szint – folyamat („process”) – A szokásos értelemben vett (teljes kontextusú) folyamat • Egy adott program dinamikus, a virtuális memóriában elhelyezkedő, „élő” példánya, lásd korábban
V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
4
Folyamatmodell (4) A Windows‐nál alkalmazott négyszintű modell Szálak állapotátmeneti diagramja
• 3. szint – szál („thread”) – Ütemezési alapegység – Lehetséges állapotok: • • • • • • •
Standby (3)
Initialized (0)
Preempció
0: Indítható („initialized”) 1: Futásra kész („ready”) 2: Futó („running”) 3: Készenléti („standby”) 4: Befejezett („terminated”) 5: Várakozó („waiting”) 6: Átmeneti („transition”)
Ready (1)
Running (2) Preempció, kvantum vége Önként lemondás
Transition (6) Kernelverem kilapozva
Waiting (5)
Terminated (4)
Original image © 2000‐2005 David A. Solomon and Mark Russinovich
• 4. szint – vékonyított szál („fiber”) – Kézi ütemezés a létrehozó szál kontextusán belül
V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
5
Folyamatok és szálak kezelése (1) Folyamatok létrehozása és megszüntetése • Folyamatok létrehozása – 3 részben fut le 3 különböző kontextusban (létrehozó folyamat, Win32 alrendszer, létrejövő folyamat)
CMD.EXE
NTVDM.EXE
MS‐DOS .bat, .cmd
Win16
program.exe Win32 Win32
Milyen típusú a program? OS/2 1.x OS2.EXE
POSIX
program.exe (speciális WoW64 támogatással)
(64 bites Windows)
MS‐DOS .exe, .com, .pif
POSIX.EXE
NTVDM.EXE
Original image © 2000‐2005 David A. Solomon and Mark Russinovich
V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
6
Folyamatok és szálak kezelése (2) Váltások a processzorok birtoklásában • Kontextusváltás – Mindig szálak között történik
T1 szál Futó
Megszakítás vagy rendszerhívás
Állapotmentés (TCB1) Állapotbetöltés (TCB2)
Futásra kész vagy várakozó
Megszakítás vagy rendszerhívás
T2 szál Futásra kész vagy várakozó
Futó
Állapotmentés (TCB2) Állapotbetöltés (TCB1) Futásra kész vagy várakozó
Futó Original image © 2000‐2005 David A. Solomon and Mark Russinovich
V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
7
Ütemezés (1) A Windows rövid távú ütemezési politikája • Eseményalapú ütemezés – Nincs központi ütemező modul a kernelben
• Az ütemezés alapvetően szálszinten történik – Minden folyamathoz tartozik legalább egy szál
• Preemptív, prioritásos, körbeforgó algoritmus – Időszelet‐alapú kötelező preempció – Mindig a legmagasabb prioritású futásképes szál fut – Külön várakozó sor minden prioritási szinthez
V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
8
Ütemezés (2) A Windows általános ütemezési politikája • Preempció (futásmegszakítás) esetei: – Lejár a szál időszelete • Újraütemezéskor az addig futó szál időszelete 3‐mal csökken
– Elindul egy nagyobb prioritású szál • Az előző az időszelet megmaradt részét később visszakapja
– A szál eseményre kezd várni – A szál önként feladja a futás jogát • Szigorú értelemben véve nem minősül preempciónak
– Külső megszakítás következik be – Megszűnik a szál
V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
9
Ütemezés (3) Prioritások • Prioritások kezelése – Kétszintű rendszer (folyamatok és szálak; 0–31‐ig)
0
15 16
31 Realtime
High 13
24
Above Normal 10 Normal 8 Below Normal 6 Idle 4 V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
10
Ütemezés (3) Prioritások • Prioritások kezelése – Kétszintű rendszer (folyamatok és szálak; 0–31‐ig) Folyamatok prioritási osztályai Realtime
Szálak prioritási szintjei
High
Above Normal
Normal
Below Normal
Idle
Time Critical
31
15
15
15
15
15
Highest
26
15
12
10
8
6
Above Normal
25
14
11
9
7
5
Normal
24
13
10
8
6
4
Below Normal
23
12
9
7
5
3
Lowest
22
11
8
6
4
2
Idle
16
1
1
1
1
1
– Lapnullázó szál: 0 – Üresjárati szál: „‐1” – Windows Vista: multimédia ütemezőszolgáltatás (MMCSS) V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
11
Ütemezés (4) Időszeletek • A körbeforgó ütemezés következményei – A processzorintenzív alkalmazásoknak kedvez – Az időszelet erősen befolyásolja az észlelt teljesítményt
• Az időszelet („quantum”) hossza – A Normál prioritási osztálynál módosítható • Hossz, típus, előtérben lévő alkalmazás kiemelése • Beállítások: – –
1 időszelet = 3*x rendszerórajel‐ütem Újraütemezésnél 3‐mal csökken az időszelet hossza
• Windows Vista: a megszakításban töltött idő (igazságos módon) már nem csökkenti az időszelet hosszát V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
12
Ütemezés (5) Beépített kompenzációs mechanizmusok • Prioritás növelése („priority boosting”) – I/O művelet befejezésekor – – – –
Képernyőmeghajtó & lemezmeghajtók: 1 Soros port & hálózat: 2 Billentyűzet & egér: 6 Hangeszközök: 8
– Várakozó állapotból kilépéskor • Eseményre, szemaforra stb. várakozás: 1 • Előtérben lévő folyamat befejezte a várakozást: 2
– A szál által várt GUI (ablak) esemény beérkezésekor: 2 – Régóta készenlétben álló szálaknál • 2 időszeletnyi időre 15‐re emeli a prioritást
• Időszelet módosítása („quantum stretching”) • A prioritásnövelés idejére megkétszereződik az időszelet V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
13
Több processzor kezelése (1) Ütemezés egynél több, illetve többmagos CPU esetén • Négyféle kernelmegvalósítás: –NTOSKRNL.EXE – egy processzor •„ACPI Uniprocessor System” rendszereken régebbi CPU‐knál (Pentium I/II/III, AMD K5/K6/Athlon)
–NTKRNLPA.EXE – egy processzor, fizikai címkiterjesztés •„ACPI Uniprocessor System” rendszereken modern CPU‐knál (Pentium IV/M, Core/Core 2, AMD Athlon XP/64/Opteron)
–NTKRNLMP.EXE – több / többmagos processzor •„ACPI Multiprocessor System” rendszereken régebbi CPU‐knál
–NTKRPAMP.EXE – több / többmagos processzor, fizikai címkiterjesztés •„ACPI Multiprocessor System” rendszereken modern CPU‐knál
V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
14
Több processzor kezelése (2) Ütemezés egynél több CPU, illetve többmagos CPU esetén • Eltérések az egyprocesszoros ütemezéshez képest – A legmagasabb prioritású szálak egyike kerül futó állapotba valamelyik processzor(mag)on • Terheléskiegyenlítés csak szálszinten
– Szimmetrikus elosztás (nincs „főprocesszor” és „alárendelt processzorok”) – Windows 2003 Server: minden processzorhoz (maghoz) külön „ready” állapotú várakozósorok
V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
15
Több processzor kezelése (3) Ütemezés egynél több, illetve többmagos CPU esetén • Processzor kiválasztása futtatandó szálhoz – Processzoraffinitás: meghatározza, hogy mely processzorokon futhat egy‐egy folyamat (a hozzá tartozó szálak) • „Ideális” processzor – – –
A szál indulásakor véletlenszerűen kijelölt processzor
• „Következő” processzorok – – –
Azon processzorok, amelyeken a szál korábban már futott
• Futtatandó szál kiválasztása processzorhoz – Az ütemező igyekszik azonos processzoron tartani a szálakat
V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
16
SzPE (C#) 2007–2008, II. félév BMF NIK
Párhuzamos programozás: .NET szálak használata Szálak kezelése Új szálak indítása, szálak felfüggesztése, állapotvezérlése és leállítása, szálak prioritási szintjei Előtér‐ és háttérszálak, ThreadPool szálak
Szinkronizáció A szinkronizáció alapfogalmai, versenyhelyzet és holtpont fogalma Kölcsönös kizárás biztosítása, a „lock” utasítás, szálak bevárása és randevúja
© Miklós Árpád, BMF NIK, 2007
[email protected]
Szálak • A szálak elsődleges célja a folyamatokon belüli párhuzamosítás A folyamatok adminisztrációja és váltása igen erőforrásigényes művelet, viszont az általuk nyújtott elszigetelés szintje egy programon belül csaknem mindig szükségtelen. Ezt az ellentmondást oldják fel a szálak, amelyek elszigetelést nem nyújtanak, gyors párhuzamos végrehajtást azonban igen. P3 folyamat
P1 folyamat T11 szál
T13 szál T12 szál
P2 folyamat
T31 szál
T22 szál T21 szál T23 szál
T24 szál
• A .NET keretrendszer támogatja a szálak kezelését is A keretrendszer kihasználja az operációs rendszer száltámogatását, de a .NET szálak és az operációs rendszer szálai között nem feltétlenül létezik 1:1 megfeleltetés (egy valódi szálon a keretrendszer több szála is futhat). V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
18
A többszálúság megvalósítási lehetőségei • System.Threading.Thread osztály Lehetővé teszi szálak egyenkénti létrehozását, azonosítását, állapotvezérlését és megszüntetését. Kezelése egyszerű, viszont sok programozói munkát és pontosságot igényel.
• System.Threading.ThreadPool osztály Gyakran ismétlődő, rövid ideig tartó, erősen párhuzamos műveletekhez rendelkezésre álló „szálkészlet”, melynek használatával megtakarítható a szálak egyenkénti létrehozásának és megszüntetésének időigényes munkája. Kezelése egyszerű és hatékony, de a szálak egyéni identitását nem biztosítja.
• System.ComponentModel.BackgroundWorker osztály (később) A felhasználói felület kezelésének és a háttérben elvégzendő, esetenként igen sokáig tartó műveletek végrehajtásának szétválasztására szolgál. Kezelése igen kényelmes (az állapotváltozásokról események útján értesíti a felhasználó osztályt), ám korlátozott funkcionalitása miatt kevés célra alkalmas.
V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
19
Threading névtér • A System.Threading névtér a következő „szálfeladatokat” oldja meg: – Futás idejű kontroll – szinkronizáció – thread pooling
•
System.Threading fontosabb típusai: – Alaposztályok: Thread és ThreadPool – Felsorolások: TreadState és ThreadPriority – Osztály: Monitor – Kivételek: ThreadAbortException és ThreadInterruptedException – delegate ThreadStart, WaitCallback, TimerCallback, IOCompletionCallback, … – …
V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
20
Szálak kezelése (kivonatos referencia) • System.Threading.Thread osztály Metódusok Start() Suspend(), Resume() Abort() GetHashCode() Sleep() Join() Tulajdonságok CurrentCulture, CurrentUICulture IsBackground IsThreadPoolThread ManagedThreadID Name Priority ThreadState *
V1.0
Szál indítása Szál felfüggesztése, illetve folytatása Szál leállítása Szál azonosítójának lekérése Várakozás a megadott időintervallum elteltéig Várakozás az adott szál befejeződésére A szálhoz tartozó aktuális kultúra, illetve a szálhoz tartozó felhasználói felület kiválasztott nyelve Az adott szál háttérszál vagy előtérszál* Az adott szál a ThreadPool egyik szála‐e A szál egyedi azonosítója A szál megnevezése A szál prioritása (fontossági szintje) A szál aktuális állapota(i)
A programok futása véget ér, ha az utolsó előtérszál is lefutott (az esetleg még futó háttérszálak ekkor automatikusan megszűnnek). 2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
21
Thread osztály public sealed class Thread { public Thread(ThreadStart start);
• Konstruktor, paramétere: ThreadStart delegate
public ThreadPriority Priority {get; set;} public ThreadState ThreadState {get;}
• Aktuális állapot
public bool IsAlive {get;} public bool IsBackground {get; set;}
• Háttér
public void Start(); public static void Sleep(int time); public void Suspend(); public void Resume(); public void Join(); public void Interrupt(); public void Abort(); public static void ResetAbort(); public static Thread CurrentThread {get;}
• Szál vezérlő metódusok
• Az aktuálisan futó szál
} V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
22
ThreadStart, ThreadPriority és ThreadState public delegate void ThreadStart(); public sealed class Thread { public Thread( ThreadStart start); public ThreadPriority Priority {get; set;} public ThreadState ThreadState {get;} …
public enum ThreadState { Unstarted, Running, Background, WaitSleepJoin, SuspendRequested, Suspended, AbortRequested, Stopped }
}
V1.0
public enum ThreadPriority { Highest, AboveNormal, Normal, BelowNormal, Lowest, }
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
23
Thread – példa •
ThreadStart delegálthoz metódus
using System.Threading; public class ThreadExample { public static void RunT0() { for ( int i=0; i<10000; i++) { Console.Write( "x“ ); Thread.Sleep(100); } } //Thread készítése delegate to method RunT0 and starting it public static void Main(string[] args) { // Main thread starts a new thread which runs RunT0 method Thread t0 = new Thread( new ThreadStart(RunT0)); t0.Start(); } } V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
24
Példa új szál indítására 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 V1.0
using System; using System.Threading; class Program { static void Main(string[] args) { Console.WriteLine("Szál közvetlen létrehozása"); Console.WriteLine("Főszál (sorszáma: {0})", Thread.CurrentThread.GetHashCode()); Thread newThread = new Thread(ThreadMethod); //.NET 1.1 esetén: Thread newThread = new Thread(new ThreadStart(ThreadMethod)); newThread.Name = "Új szál"; newThread.Start(); newThread.Join(); }
}
static void ThreadMethod() { Console.WriteLine("{0} (sorszáma: {1})", Thread.CurrentThread.Name, Thread.CurrentThread.GetHashCode()); }
2008. március 10.
ThreadExamples\Program.cs © Miklós Árpád, BMF NIK, 2007
[email protected]
25
Thread – példa using System; Két szál, melyek folyamatosan using System.Threading; a képernyőre írnak class Printer { char ch; int sleepTime; public Printer(char c, int t) {ch = c; sleepTime = t;} public void Print() { for (int i = 0; i < 100; i++) { Console.Write(ch); Thread.Sleep(sleepTime); } } } class Test { static void Main() { Printer a = new Printer('.', 10); Printer b = new Printer('*', 100); new Thread(a.Print).Start(); new Thread(b.Print).Start(); } } V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
26
Előtér és háttér szálak • Az előtér és háttér szálak közötti különbségek – Amíg előtér szál fut, addig a program nem terminál – A háttér szál futása nem gátolja meg a program terminálását • Az IsBackground tulajdonsággal állítható
Thread bgThread = new Thread(new ThreadStart(…)); bgThread.IsBackground = true;
V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
27
Thread állapotok – példa Thread t = new Thread(P); Console.WriteLine("name={0}, priority={1}, state={2}", t.Name, t.Priority, t.ThreadState); t.Name = "Worker"; t.Priority = ThreadPriority.BelowNormal; t.Start(); Thread.Sleep(1); Console.WriteLine("name={0}, priority={1}, state={2}", t.Name, t.Priority, t.ThreadState); t.Suspend(); Thread.Sleep(1); Console.WriteLine("state={0}", t.ThreadState); t.Resume(); Console.WriteLine("state={0}", t.ThreadState); public enum ThreadState { t.Abort(); Unstarted, Thread.Sleep(1); Running, Console.WriteLine("state={0}", t.ThreadState);
Background, WaitSleepJoin, SuspendRequested, Suspended, AbortRequested, Stopped
Output name=, priority=Normal, state=Unstarted name=Worker, priority=BelowNormal, state=Running state=Suspended state=Running state=Stopped
V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
}
28
Thread állapotdiagram Suspended safepoint reached
t.Resume Stopped
SuspendRequested end of thread method
t.Suspend Unstarted
t.Start
Thread.Sleep other.Join Wait(obj)
t.Abort
Running time over other joined Pulse(obj)
Exception caught, finally processed
AbortRequested
t.Interrupt o rt b t.A
WaitSleepJoin
V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
29
Megvárás – példa (Join) using System; using System.Threading; class Test { static void P() { for (int i = 0; i < 20; i++) { Console.Write('-'); Thread.Sleep(100); } } static void Main() { Thread t = new Thread(P); Console.Write("start"); t.Start(); t.Join(); // waits for t Console.WriteLine("end"); } } Output: start--------------------end V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
30
Abort kezelése – példa Az Abort kivételt dob, amelyet kezelni lehet a metódusban using System; using System.Threading; class Test { static void P() { try { try { try { while (true) ; } catch (ThreadAbortException) { Console.WriteLine("-- inner aborted"); } } catch (ThreadAbortException) { Console.WriteLine("-- outer aborted"); } } finally { Console.WriteLine("-- finally"); } } static void Main(string[] arg) { Thread t = new Thread(P); t.Start(); Thread.Sleep(1); t.Abort(); t.Join(); Console.WriteLine("done"); } } Output -- inner aborted -- outer aborted -- finally done V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
31
ThreadPools • ThreadPool szálak halmazát képes kezelni – Regisztrált feladathalmaz hatékony kezelése – Rövid idejű feladatok, inaktívak egy ideig
• Hátrány: – Az alkalmazói programnak nincs kontrollja (nem lehet pl. prioritást szabályozni)
V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
32
Szálak kezelése (kivonatos referencia) • System.Threading.ThreadPool osztály Metódusok QueueUserWorkItem()
Metódus végrehajtása egy ThreadPool szálon A rendelkezésre álló ThreadPool szálak számának GetAvailableThreads() lekérdezése GetMaxThreads(), Maximálisan rendelkezésre álló, illetve minimálisan GetMinThreads() életben tartott ThreadPool szálak számának lekérdezése SetMaxThreads(), Maximálisan rendelkezésre álló, illetve minimálisan SetMinThreads() életben tartott ThreadPool szálak számának beállítása RegisterWaitForSingleObject() Várakozás erőforrásra vagy időzítőre
V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
33
ThreadPool osztály public sealed class ThreadPool { public static void GetAvailableThreads(out int w, out int aIOs);
• A munka és IO szálak száma
public static void GetMaxThreads(out int w, out int aIOs);
• A munka és IO szálak maximális száma
public static bool QueueUserWorkItem( WaitCallback task); public static bool QueueUserWorkItem( WaitCallback task, object state);
• A task regisztrálása WaitCallback delegate-ként
} public delegate void WaitCallback(object state );
V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
• WaitCallback delegate
34
ThreadPool – példaváz • Feladat definíció public static void WorkerTask(object state) { while (…) { … // do something short Thread.Sleep(…); // then sleep } }
• A munka és IO szálak száma int maxWorkers, availWorkers; int maxIOs, availIOs; ThreadPool.GetMaxThreads(out maxWorkers, out maxIOs); ThreadPool.GetAvailableThreads(out availWorkers, out availIOs);
• Új feladatot adunk a poolba object state = …; ThreadPool.QueueUserWorkItem(new WaitCallback(WorkerTask), state); V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
35
Feladat (3) Készítsünk konzolos alkalmazást, amely a ThreadPool osztály segítségével 4 külön szálon jeleníti meg az egyes szálak által folyamatosan növelt saját belső számláló értékét! A program valamilyen megoldással biztosítsa a szálak által kiírt adatok vizuális elkülönítését! Ötletek: – A QueueUserWorkItem() metódus paramétere egy WaitCallback típusú képviselő, amely visszatérési érték nélküli, egyetlen („object” típusú) paraméterrel rendelkező metódusokat képes tárolni – A ThreadPool szálai is azonosíthatók a ManagedThreadID tulajdonsággal (azonban előfordulhat például, hogy egy szál által megkezdett feladatot egy másik szál folytat és egy harmadik szál fejez be) – A szálak közötti váltások megfigyeléséhez érdemes sok munkát adni az egyes szálaknak és néha várakoztatni őket (ennek legegyszerűbb módja a Thread osztály statikus Sleep() metódusa) V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
36
Megoldás (3)
ThreadExamples\Program.cs V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
37
BackgoundWorker szálak (rövid referencia) • System.ComponentModel.BackgroundWorker osztály Metódusok RunWorkerAsync() CancelAsync() ReportProgress Tulajdonságok IsBusy CancellationPending WorkerSupportsCancellation WorkerReportsProgress Események DoWork ProgressChanged RunWorkerCompleted
*
V1.0
Háttérszál indítása Háttérszál leállítása Háttérszál folyamatjelzése A háttérszál aktív‐e (éppen fut‐e) Leállítás folyamatban (leállási kérelem érkezett) A háttérszál kérés esetén képes idő előtti leállásra A háttérszál képes folyamatjelzésre Kezelője a háttérben futtatandó metódus* A háttérszál folyamatjelzését fogadó esemény A háttérszál futása befejeződött
Ez a metódus (a Windows UI megvalósítási modellje következtében) közvetlenül nem érintkezhet a felhasználói felület elemeivel. Ezek szükséges frissítését és állapotmódosításait a ProgressChanged és a RunWorkerCompleted eseménykezelőkben lehet elvégezni. 2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
38
Feladat (4) Készítsünk Windows alkalmazást, amely időigényes műveletet futtat a háttérben, a felhasználói felülettől független szálon! A program legyen képes a művelet indítására és menet közbeni biztonságos megszakítására, a háttérben futó művelet állapotát pedig folyamatjelzővel jelezze! Ötletek: – Célszerű a BackgroundWorker osztály segítségével megoldani a feladatot – A háttérben futó szál a ReportProgress() metódussal jelezheti az előrehaladást (ezt az adatot a ProgressChanged esemény második paraméterében kapja meg a megfelelő eseménykezelő metódus) – A művelet megszakítását csak akkor kíséreljük meg, ha valóban fut (ez az IsBusy tulajdonság vizsgálatával állapítható meg) – A háttérben futó művelet végén a RunWorkerCompleted esemény kezelője a második paraméterben kap információt a műveletről (véget ért‐e vagy megszakítás miatt fejeződött be, mi a végeredmény, történt‐e hiba stb.) V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
39
Megoldás (4)
BackgroundWorker V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
40
Szinkronizáció • A szinkronizáció olyan, párhuzamos szálak (vagy folyamatok) együttműködését megvalósító mechanizmus, amely minden körülmények között biztosítja a szálak (vagy folyamatok) által végzett műveletek szemantikai helyességét A párhuzamosan futó szálak kommunikációjához szinte biztosan szükség van közös erőforrások (memória, portok, I/O eszközök, fájlok) használatára. Ha ezek állapotát egy szál módosítja, de közben más szálak is hozzájuk férnek, akkor az utóbbi szálak könnyen hibás vagy félkész adatokhoz juthatnak.
• Az alapprobléma: bármely két utasítás végrehajtása között előfordulhat, hogy más szálak kapnak lehetőséget az előző szál által is kezelt közös adatok olvasására vagy módosítására Egyprocesszoros rendszereknél az operációs rendszer ütemezője (a szálak közötti váltás) ad erre lehetőséget, többprocesszoros rendszereknél pedig a valódi (fizikai) párhuzamosság miatt még gyakrabban merül fel a probléma. Ennek elkerülését szolgálják elsősorban a különböző szinkronizációs megoldások (másik, ezzel összefüggő céljuk az időzítések összehangolása). V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
41
Szinkronizáció kölcsönös kizárással • Kritikus szakasz („critical section”) A programokon belül megjelölt kritikus kódrészletek soros végrehajtását biztosítja több párhuzamos szál esetén is. .NET osztályok: System.Threading.Monitor (és a C# „lock” utasítása), System.Threading.Mutex, System.Threading.ReaderWriterLock
• Szemafor („semaphore”) A kritikus szakasz általánosítása (többpéldányos erőforrások esetén egyszerre több szál belépését is lehetővé teszi). .NET osztály: System.Threading.Semaphore (.NET 2.0)
• Atomi végrehajtás („interlocked execution”) Egyes egyszerű műveletek oszthatatlan végrehajtását biztosítja (igen gyors). .NET osztály: System.Threading.Interlocked
• Csővezeték („pipe”) Olvasható és írható FIFO puffer, amely szükség szerint várakoztatja az igénylőket (az ún. „termelő‐fogyasztó” probléma megoldására készült). V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
42
Szinkronizáció bevárással (randevú) • Esemény („event”) Két kódrészlet soros végrehajtását biztosítja úgy, hogy a „B” kódrészletet végrehajtó szál megvárja, amíg az „A” kódrészletet végrehajtó szál végez feladatával, illetve lehetőséget ad alkalmankénti vagy rendszeres jelzésre is. .NET osztályok: System.Threading.Thread, System.Threading.AutoResetEvent, System.Threading.ManualResetEvent
• Időzítő („timer”) Relatív vagy abszolút időhöz való igazodást tesz lehetővé. .NET osztályok: System.Windows.Forms.Timer, System.Timers.Timer, System.Threading.Timer Időzítők jellemzői Windows.Forms.Timer Pontosság ~10 ms Futtatás saját szálon – Csak egyszeri aktiválás – – Beállítható első aktiválás Vizuális komponens + Platformfüggetlen – V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
Timers.Timer ~100 ns (!) + + – – –
Threading.Timer ~1 ms + + + – + 43
Kölcsönös kizárás lock utasítás lock(Variable) Statement
Példa: class Account { long val = 0; public void Deposit(long x) { lock (this) { val += x; } }
// this class is a monitor
// only 1 thread at a time may execute this statement
public void Withdraw(long x) { lock (this) { val -= x; } } }
A Lock bármilyen objektumra állítható, gyakran – hibásan – a this‐t használják object semaphore = new object(); ... lock (semaphore) { ... critical region ... }
V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
44
Példa szinkronizációra (a „lock” utasítás) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 V1.0
using System; using System.Threading; class Program { private static int counter = 0; private static object lockObject = new Object();
Figyelem: SOHA ne írjunk le az alábbiakra hasonlító kódot:
lock (this)
static void Main(string[] args) { Thread t1 = new Thread(ThreadMethod); t1.Start(); Thread t2 = new Thread(ThreadMethod); t2.Start(); }
vagy
lock (typeof(Program))
A lock utasítás nélkül a metódus
}
private static void ThreadMethod() sorosan (egy szálon futtatva) { helyesen működik, párhuzamosan lock (lockObject) (több szálon) azonban nem { counter++; Thread.Sleep(500); Console.WriteLine("A számláló állása: " + counter); } } 2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
45
A szinkronizáció két alapvető nehézsége • Versenyhelyzet („race condition”) Párhuzamos futtatás esetén a közösen használt erőforrásokhoz történő hozzáférés szabályozatlansága veszélyezteti a program helyes működését. Az előző példa a lock utasítás nélkül jól illusztrálja a versenyhelyzet fogalmát.
• Holtpont („deadlock”) Akkor léphet fel holtpont, ha több szál több erőforráshoz kíván hozzáférni, miközben egyes erőforrásokat lefoglalva tartanak (tehát már beléptek egy erőforráshoz tartozó kritikus szakaszba, ott viszont várakozniuk kell, hogy hozzájuthassanak egy másik szükséges erőforráshoz). Példa: 1 lock (a) 2 { // feldolgozás 3 lock (b) 4 { 5 // feldolgozás 6 } 7 8 } V1.0
2008. március 10.
1. szál
1 lock (b) 2 { // feldolgozás 3 lock (a) 4 { 5 // feldolgozás 6 } 7 8 }
© Miklós Árpád, BMF NIK, 2007
[email protected]
2. szál
46
Monitor osztály A Monitor osztály alapszintű szinkronizációt biztosít
•
public sealed class Monitor { public static void Enter(object obj); public static bool TryEnter(object obj); public static void Exit(object obj); public static void Wait(object obj); public static bool Pulse(object obj); public static void PulseAll(object obj); }
• Az obj részére lock‐kolást próbál biztosítani és blokkol • Az obj részére lock‐kolást próbál biztosítani és visszatér • Feloldja a lock‐kolást • Várakozás állapotba hozza a szálat és elengedi a lock‐ot • Felébreszti a következő obj‐ra váró szálat • Felébreszti az összes obj‐ra váró szálat
• lock a Monitor rövid formája: lock (obj) { … } V1.0
2008. március 10.
Î
Monitor.Enter(obj) try { … } finally { Monitor.Exit(obj) }
© Miklós Árpád, BMF NIK, 2007
[email protected]
47
Monitor használata Enter blokkol, ha a lock‐kolás nem biztosítható TryEnter blokkolás nélkül próbál lock‐kolni; hamissal tér vissza, ha nem elérhető a lock‐kolás
• •
Enter: blokkolással!
TryEnter: blokkolás nélkül
public class MonitorExample { private Queue lpt;
public bool AddElemNonBlocking (object elem) { try { if (! Monitor.TryEnter (lpt.SyncRoot)) return false; lpt.Enqueue (elem); } catch (Exception e) { … } finally { Monitor.Exit (lpt.SyncRoot); } return true; }
public void AddElemBlocking (object elem) { try { Monitor.Enter (lpt.SyncRoot); lpt.Enqueue (elem); } catch (Exception e) { … } finally { Monitor.Exit (lpt.SyncRoot); } } } V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
48
Wait és Pulse • A Wait és Pulse segítségével a szálak szinkronizálhatók
A lock‐kolás elengedése és várakozás ébredésre public static void Wait(object obj); public static bool Wait(object obj, int millies);
Az obj‐re váró következő, vagy összes szál felébresztése public static bool Pulse(object obj); public static void PulseAll(object obj);
lock (obj) { ... Monitor.Wait(obj); ... } V1.0
2008. március 10.
lock (obj) { ... Monitor.Pulse(obj); ... } © Miklós Árpád, BMF NIK, 2007
[email protected]
49
Wait és Pulse – elvi példa Thread A 1 lock(v) { ... 2 Monitor.Wait(v); 5 ... }
Thread B 3 lock(v) { ... 4 Monitor.Pulse(v); ... } 6
1. A eléri lock(v)‐t és lock‐kol, mert a kritikus régió szabad 2. A eléri Wait utasítást, aludni megy és felengedi a lock‐ot 3. B eléri lock(v)‐t és lock‐kol, mert a kritikus régió szabad 4. B eléri a Pulse‐t és felébreszti A‐t. (Lehetséges kontextusváltás A és B között, de nem szüségszerű) 5. A lock‐ot szeretne kapni, de nem tud, mert B még kritikus régióban van 6. A kritikus régió végén B felengedi a lock‐ot; A tovább futhat
V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
50
Wait és Pulse – szinkronizált puffer példa Ha a termelő gyorsabb: Put Put Put Get Put Get ...
public class Buffer { const int size = 16; char[ ] buf = new char[size]; int head = 0, tail = 0, n = 0; public void Put(char ch) { lock(this) { while (n >= size) Monitor.Wait(this); buf[tail] = ch; tail = (tail + 1) % size; n++; Monitor.Pulse(this); } } public char Get() { lock(this) { while (n <= 0) Monitor.Wait(this); char ch = buf[head]; head = (head + 1) % size; n--; Monitor.Pulse(this); return ch; } }
Wake up waiting threads
thread 2
Lock buffer to retrieve character While buffer is empty, release lock and wait Wake up waiting threads
}
V1.0
2008. március 10.
thread 1
Lock buffer to add a character While buffer is full, release lock and wait
© Miklós Árpád, BMF NIK, 2007
[email protected]
Ha a fogyasztó a lassabb: Put Get Put Get ...
51
Szinkronizált puffer 3 Get szál eléri az üres puffert
Belépnek a kritikus régióba és aludni mennek, mert üres a puffer
Egy Put szál érkezik, Kritikus régióba lép; Elhelyezi adatát és jelez: PulseAll
G1 G2
P1
G3
Első belépés
Kritikus régió
G1 G2 G3
G1 G2 G3
Várakozás Mindenl Get szál felébred; Az első a kritikus régióba lép, kiolvassa az adatot és kilép
G1 G2 G3
Ismételt belépés V1.0
2008. március 10.
A többiek ismét várakozási állapotba kerülnek, mert üres a puffer
G2 G3 © Miklós Árpád, BMF NIK, 2007
[email protected]
52
Feladat (5) Készítsünk többszálú konzolos alkalmazást, amely egy időigényes számítási műveletet 2 szállal párhuzamosan végeztet el! A műveletet most egy közösen használt számláló folyamatos növelése jelentse, és egy szálnak kb. 2‐3 másodpercig tartson a művelet elvégzése! Amennyiben szükséges, gondoskodjon a szálak szinkronizációjáról is! A program valamilyen megoldással biztosítsa a szálak által kiírt adatok vizuális elkülönítését! Ötletek: – A Stopwatch osztály metódusai segítségével egyszerűen mérhető az eltelt (relatív) idő – Először egy egyszerű megvalósítással döntsük el, szükség van‐e szinkronizációra, majd ha úgy ítéljük meg, hogy igen, akkor használjuk a lock utasítást vagy a Monitor osztály statikus Enter(), illetve és Exit() metódusát – Szinkronizáció esetén a jobb teljesítmény érdekében igyekezzünk a lehető legrövidebbre venni a kritikus szakaszt V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
53
Irodalomjegyzék (alapismeretek) • C. Nagel, B. Evjen, J. Glynn, M. Skinner, K. Watson, A. Jones: Professional C# 2005 Kiadó: Wiley Publishing, Inc., 2006 ISBN: 978‐0‐7645‐7534‐1 Web: http://www.wiley.com/, http://www.wrox.com/ Nyelv: angol Terjedelem: 1540 oldal
Folyamatok kezelése: 14–16., 413. o. Szálkezelés: 349–368. o.
• Microsoft Corp., Visual Studio Developer Center Szálkezelés a .NET keretrendszerben: http://msdn2.microsoft.com/en‐us/library/3e8s7xdd(VS.80).aspx
V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
54
Irodalomjegyzék (magasszintű ismeretek) • Albert I., Balássy Gy., Charaf H., Erdélyi T., Horváth Á., Levendovszky T., Péteri Sz., Rajacsics T.: A .NET Framework és programozása Kiadó: Szak Kiadó, 2004 ISBN: 963‐9131‐62‐8 Web: http://www.szak.hu/ Nyelv: magyar Terjedelem: 868 oldal
Párhuzamos programozás: 585–607. o. Folyamatok kezelése: 608–613. o. Szálkezelés és szinkronizáció: 614–648. o.
• J. Richter: CLR via C#, Second Edition Kiadó: Microsoft Press, 2006 ISBN: 978‐0‐7356‐2163‐3 Web: http://www.wintellect.com/ Nyelv: angol Terjedelem: 736 oldal
Szálkezelés és szinkronizáció: 585–648. o. V1.0
2008. március 10.
© Miklós Árpád, BMF NIK, 2007
[email protected]
55