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 miklos.arpad@nik.bmf.hu 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. 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. 3 Illusztráció: az időosztás elve Futó állapot P 1 folyamat Megszakítás vagy rendszerhívás Állapotmentés (PCB 1 ) Állapotbetöltés(PCB 2 ) P 2 folyamat Futásra kész vagy várakozó állapot Futásra kész vagy várakozó állapot Futó állapot Megszakítás vagy rendszerhívás Állapotmentés (PCB 2 ) Állapotbetöltés(PCB 1 ) Futó állapot Futásra kész vagy várakozó állapot Original image 2000-2005 David A. Solomon and Mark Russinovich 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 * 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ó. 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.ProcessStartInfoosztá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. 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 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) 7 Folyamatok kezelése (kivonatos referencia) System.Diagnostics.ProcessStartInfo osztály Tulajdonságok FileName Arguments, WorkingDirectory Domain, UserName, Password RedirectStandardInput, RedirectStandardOutput ErrorDialog UseShellExecute Verb WindowStyle 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 Folyamat indítása adott felhasználó nevében Alapértelmezett be- és kimeneti csatorna átirányítása Hibaüzenet jelenjen-e meg, ha a folyamat indítása sikertelen Operációs rendszerhéj programindítófunkciójának használata folyamat indításához A társított fájl megnyitásakor végrehajtandó művelet Kezdeti ablakméret megadása (normál, minimalizált vagy maximalizált méret) 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 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. 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 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\v1\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. miklos.arpad@nik.bmf.hu 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 12 6
Megoldás (2) VisualCompiler 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. P 1 folyamat T 11 szál T 13 szál P 2 folyamat T 22 szál P 3 folyamat T 31 szál T 12 szál T 21 szál T 23 szál T 24 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). 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. 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 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). 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 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 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. 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 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()); ThreadExamples\Program.cs 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) 20 10
Megoldás (3) ThreadExamples\Program.cs 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.) 22 11
Megoldás (4) BackgroundWorker 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). 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 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). 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 Timers.Timer ~100 ns (!) + + Threading.Timer ~1 ms + + + + 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 using System; using System.Threading; class Program private static int counter = 0; private static object lockobject = new Object(); static void Main(string[] args) Thread t1 = new Thread(ThreadMethod); t1.start(); Thread t2 = new Thread(ThreadMethod); t2.start(); private static void ThreadMethod() lock (lockobject) counter++; Thread.Sleep(500); Console.WriteLine("A számláló állása: " + counter); A lock utasítás nélkül a metódus sorosan (egy szálon futtatva) helyesen működik, párhuzamosan (több szálon) azonban nem Figyelem: SOHAne írjunk le az alábbiakra hasonlító kódot: lock (this) vagy lock (typeof(program)) 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 3 // feldolgozás 4 lock (b) 5 6 // feldolgozás 7 8 1. szál 1 2 3 4 5 6 7 8 lock (b) // feldolgozás lock (a) // feldolgozás 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 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 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. 32 16