Haladó programozás (C#) 2007–2008, I. félév BMF NIK
Párhuzamos programozás: folyamatok és szálak A párhuzamos programozás alapjai A végrehajtás szétválasztása: elszigetelés és párhuzamosítás Párhuzamosság és ütemezés az operációs rendszer szintjén
Folyamatok fogalma és kezelése Új folyamat indítása, létező folyamatok leállítása és bevárása Folyamatok tulajdonságainak és eseményeinek kezelése
Szálak fogalma és 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]
Hallgatói tájékoztató A jelen bemutatóban található adatok, tudnivalók és információk a számonkérendő anyag vázlatát képezik. Ismeretük szükséges, de nem elégséges feltétele a sikeres zárthelyinek, illetve vizsgának. Sikeres zárthelyihez, illetve vizsgához a jelen bemutató tartalmán felül a kötelező irodalomként megjelölt anyag, a gyakorlatokon szóban, illetve a táblán átadott tudnivalók ismerete, valamint a gyakorlatokon megoldott példák és az otthoni feldolgozás céljából kiadott feladatok önálló megoldásának képessége is szükséges.
V1.0
2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
2
1
A párhuzamos végrehajtás alapjai • A Neumann-architektúrára épülő számítógépek a programokat sorosan hajtják végre A gépi kódú programok tényleges futtatása utasításról utasításra történik. Ahhoz, hogy egy számítógépen egyszerre több program is futtatható legyen, olyan megoldásra van szükség, amely biztosítja – a végrehajtás alatt álló programok egymástól való elszigetelését, valamint – a végrehajtás alatt álló programok (látszólag) egyidejű futását.
• A programok elszigetelése és párhuzamosítása a folyamatok koncepciójának segítségével megoldható Az elszigetelés érdekében minden folyamat saját memóriaterülettel rendelkezik, amelyet más folyamatok nem érhetnek el, így hiba esetén csak a hibázó folyamat sérül, a rendszer többi eleme működőképes marad (viszont a folyamatok közötti közvetlen kommunikációra sincs egyszerű lehetőség). A párhuzamosítás tipikus megoldása az időosztás, amikor minden folyamat kap egy-egy ún. időszeletet, melynek leteltét követően egy másik folyamat kapja meg a vezérlést. Ez a megoldás gyakoratilag függetleníti egymástól a processzorok és a rajtuk egyidőben futtatható programok számát. V1.0
© Miklós Árpád, BMF NIK, 2007
[email protected]
2007. szeptember 7.
3
Illusztráció: az időosztás elve P1 folyamat
P2 folyamat Megszakítás vagy rendszerhívás
Futó állapot
Állapotmentés (PCB1)
Futásra kész vagy várakozó állapot
Állapotbetöltés (PCB2) Futásra kész vagy várakozó állapot
Futó állapot Megszakítás vagy rendszerhívás
Állapotmentés (PCB2) Futó állapot
Állapotbetöltés (PCB1)
Futásra kész vagy várakozó állapot
Original image © 2000-2005 David A. Solomon and Mark Russinovich
V1.0
2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
4
2
Ütemezés az operációs rendszerekben • Az ütemezés fogalma a végrehajtás alatt álló programok közötti rendszeres váltás módját, időzítését és szabályait takarja A Microsoft Windows preemptív, prioritásos, körbenforgó, kétszintű* ütemezési politikát alkalmaz. • Az ütemezés „preemptív”: az operációs rendszer kívülről bármikor képes megszakítani a programok futását • Az ütemezés „prioritásos”: minden programnak van egy fontossági szintje (prioritása), amely meghatározza, hogy egy-egy időszelet lejártakor melyik program következhet sorra • Az ütemezés „körbenforgó”: az egyforma prioritású programok között a rendszer egyenlően osztja el a rendelkezésre álló időt, és a programok sorban egymás után kapnak egy-egy időszeletet (az utolsó program időszelete után ismét az első következik) • Az ütemezés „kétszintű”: az elszigetelt folyamatok mellett léteznek ún. szálak is, amelyek a futtatni kívánt kód egy folyamaton belüli további bontását teszik lehetővé (lásd később)
A Unix/Linux rendszerek félig preemptív, prioritásos, körbenforgó, egyszintű ütemezési politikát alkalmaznak. • Az ütemezés „félig preemptív”: az operációs rendszer a programok futását kívülről bármikor képes megszakítani, saját belső elemeinek futását azonban nem • Az ütemezés „egyszintű”: a rendszerben csak elszigetelt folyamatok léteznek, ezek képezik a párhuzamosítás alapegységét is *
V1.0
A modell ténylegesen négyszintű, de a legfelső („job”) és a legalsó („fiber”) szemcsézettségi szint csak speciális módon használható. 2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
5
Folyamatok • A .NET keretrendszerben a folyamatok megfelelnek az operációs rendszer folyamatainak A folyamatok kezelését a System.Diagnostics.Process és a System.Diagnostics.ProcessStartInfo osztályok biztosítják. A Process osztály segítségével új folyamatok hozhatók létre, létező folyamatok szüntethetők meg és a folyamatokról részletes adatok érhetők el. A ProcessStartInfo osztály segítségével számos paraméter és beállítás adható meg a folyamatként elindítani kívánt programokhoz.
• A .NET a folyamatokon belül egy további szintet, az ún. alkalmazástartományt („application domain”) is meghatároz A felügyelt kódú programokat a keretrendszer futtatás közben is ellenőrzi, ezért ezek nem képesek egymást negatívan befolyásolni. Így viszont nem feltétlenül szükséges külön folyamatként futtatni őket, ami sebesség és memóriaigény szempontjából nagy előny, mivel a folyamatok létrehozása, nyilvántartása és a közöttük történő váltás sok időt és memóriát igényel. Az alkalmazástartományokkal és programozásukkal a jelen tárgy keretében nem foglalkozunk részletesen. V1.0
2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
6
3
Folyamatok kezelése (kivonatos referencia) • System.Diagnostics.Process osztály Metódusok Start() CloseMainWindow() Kill() GetCurrentProcess() GetProcesses() WaitForExit() Tulajdonságok StartInfo PriorityClass EnableRaisingEvents HasExited ExitCode, ExitTime StandardInput, StandardOutput Események Exited V1.0
2007. szeptember 7.
Folyamat indítása Folyamat főablakának bezárása (GUI alkalmazásoknál) Folyamat leállítása Aktuális folyamatot reprezentáló objektum lekérése Összes folyamat adatainak lekérése a helyi számítógépről Várakozás az adott folyamat befejeződésére A folyamathoz tartozó ProcessStartInfo példány A folyamat prioritása (fontossági szintje) A folyamat kiválthat-e eseményeket A folyamat kilépett-e Kilépési kód, illetve a kilépés (vagy leállítás) időpontja Alapértelmezett be- és kimeneti csatorna (adatfolyam) A folyamat kilépett (vagy leállították) © Miklós Árpád, BMF NIK, 2007
[email protected]
7
Folyamatok kezelése (kivonatos referencia) • System.Diagnostics.ProcessStartInfo osztály Tulajdonságok FileName
Fájlnév megadása az indítandó folyamathoz (program vagy programmal társított fájltípusba tartozó fájl neve) Parancssori paraméterek és munkakönyvtár megadása az indítandó folyamathoz
Arguments, WorkingDirectory Domain, UserName, Folyamat indítása adott felhasználó nevében Password RedirectStandardInput, Alapértelmezett be- és kimeneti csatorna átirányítása RedirectStandardOutput ErrorDialog Hibaüzenet jelenjen-e meg, ha a folyamat indítása sikertelen Operációs rendszerhéj programindító funkciójának használata UseShellExecute folyamat indításához Verb A társított fájl megnyitásakor végrehajtandó művelet Kezdeti ablakméret megadása (normál, minimalizált vagy WindowStyle maximalizált méret)
V1.0
2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
8
4
Példa új folyamat 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
using System; using System.Diagnostics; class Program { static void Main() { Process newProcess = new Process(); newProcess.StartInfo = new ProcessStartInfo("hello.exe", "Pistike"); newProcess.StartInfo.ErrorDialog = true; newProcess.StartInfo.UseShellExecute = false; newProcess.StartInfo.RedirectStandardOutput = true; newProcess.Start(); newProcess.WaitForExit(); Console.WriteLine("Az elindított folyamat üzenetei:"); Console.Write(newProcess.StandardOutput.ReadToEnd()); Console.ReadLine(); } } ProcessExamples\Program.cs
V1.0
2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
9
Feladat (1) Készítsünk konzolos alkalmazást, amely (a számítógépre telepített .NET keretrendszer segítségével) képes a parancssorban megadott C# nyelvű forrásfájl lefordítására és az esetleges hibák megjelenítésére! Amennyiben a forráskód hibátlan volt, ezt a program külön üzenetben jelezze! Ötletek: – A .NET keretrendszer része a parancssoros C# fordító (csc.exe) – A program számára parancssorban megadott adatok kezeléséhez a Main() metódus „args” paraméterét használhatjuk fel, amely karaktersorozatok tömbjeként tartalmazza az átadott adatokat – Ha a forráskód fordítása sikeres, maga a C# fordító egy néhány soros fejlécen kívül semmilyen üzenetet nem ír ki. A fejléc megjelenítése a „/nologo” parancssori paraméterrel kapcsolható ki.
V1.0
2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
10
5
Megoldás (1) 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 27 28 V1.0
using System; using System.Diagnostics; class Program { static void Main(string[] args) { if (args.Length > 0) { ProcessStartInfo startinfo = new ProcessStartInfo(); startinfo.FileName = String.Format(@"{0}\..\Microsoft.NET\Framework\v{1}\csc.exe", Environment.GetFolderPath(Environment.SpecialFolder.System), Environment.Version.ToString(3)); startinfo.Arguments = String.Format(@"/nologo /t:exe {0}", args[0]); startinfo.RedirectStandardOutput = true; startinfo.UseShellExecute = false; Process compilerProcess = Process.Start(startinfo); string output = compilerProcess.StandardOutput.ReadToEnd(); compilerProcess.WaitForExit(); if (output == String.Empty) Console.WriteLine("A forráskód hibátlan, a fordítás sikerült."); else Console.WriteLine("Hibaüzenetek:" + Environment.NewLine + output); } } }
Compiler\Program.cs 2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
11
Feladat (2) Készítsük el az előző feladat bővített megfelelőjét grafikus Windows alkalmazásként is (az elkészült program adjon lehetőséget a forráskód szerkesztésére, betöltésére, mentésére, fordítására és futtatására)! Ötletek: – A program a forráskódot fordítás előtt mentse el egy ideiglenes fájlba – A futtatáshoz használjuk a Process osztály statikus Start() metódusát
V1.0
2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
12
6
Megoldás (2)
VisualCompiler V1.0
© Miklós Árpád, BMF NIK, 2007
[email protected]
2007. szeptember 7.
13
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
2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
14
7
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 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
2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
15
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). 2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
16
8
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
2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
17
Szálak kezelése (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. 2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
18
9
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()); } } 2007. szeptember 7.
ThreadExamples\Program.cs © Miklós Árpád, BMF NIK, 2007
[email protected]
19
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
2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
20
10
Megoldás (3)
ThreadExamples\Program.cs V1.0
2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
21
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
2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
22
11
Megoldás (4)
BackgroundWorker V1.0
2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
23
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
2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
24
12
Példa „egyszerű” C# kód összetettségére A sötétebb színű vonalak olyan lehetséges megszakítási pontokat jelölnek, amelyek szinkronizációs problémákat okozhatnak
V1.0
2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
25
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
2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
26
13
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
2007. szeptember 7.
Timers.Timer ~100 ns (!) + + – – –
Threading.Timer ~1 ms + + + – +
© Miklós Árpád, BMF NIK, 2007
[email protected]
27
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); } } } 2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
28
14
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
2007. szeptember 7.
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
29
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
2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
30
15
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
2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
31
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
2007. szeptember 7.
© Miklós Árpád, BMF NIK, 2007
[email protected]
32
16