310 likes | 399 Views
Fonalak . NET-ben. Krizsán Zoltán. Mikor, miért használjuk?. Ha az algoritmus sokáig dolgozik, d e el akarjuk kerülni a „fagyást”. Kisebb a költsége, mint az új folyamatnak. Programozás szempontjából is könnyebb, mint az IPC (fájl, osztott memória, …). Thread osztály. Létrehoz, elindít
E N D
Fonalak .NET-ben Krizsán Zoltán
Mikor, miért használjuk? • Ha az algoritmus sokáig dolgozik, • de el akarjuk kerülni a „fagyást”. • Kisebb a költsége, mint az új folyamatnak. • Programozás szempontjából is könnyebb, mint az IPC (fájl, osztott memória, …)
Thread osztály • Létrehoz, • elindít • szálat
Tulajdonságok • IsAliveGets a value indicating that the current thread is currentlyexecuting. • IsBackgroundGets or sets whether the thread runs as a backgroundthread. • IsThreadPoolThreadGets whether this thread is a thread in the thread pool. • ManagedThreadIdGets a number to identify the current thread. Not the sameas the operating system’s thread ID. • Name Gets or sets a name associated with the thread. • Priority Gets or sets the priority of the thread. • ThreadStateGets the ThreadStatevalue for the thread.
Metódusok • Abort Raises a ThreadAbortExceptionon the thread to indicate thatthe thread should be aborted. • Interrupt Raises a ThreadInterruptedExceptionwhen a thread is in ablocked state (ThreadState.WaitSleepJoin).If the threadnever blocks, the interruption never happens. • Join Blocks the calling thread until the thread terminates. • Resume Deprecated. Do not use. • Start Sets a thread to be scheduled for execution. • Suspend Deprecated. Do not use.
Fonál létrehozása • Definiáljunk egy metódust! (nincs vé, nincs paraméter) • Hozzunk létre egy ThreadStartdelegátum példányt! • Hozzunk létre egy új Threadpéldányt megadva az előző delegátumot! • Hívjuk meg a start metódust!(A fonálba fut a 1. metódus, majd leáll.)
Fonál létrehozás példakód 1 static void SimpleWork() { Console.WriteLine("Thread: {0}", Thread.CurrentThread.ManagedThreadId); } ThreadStartoperation = new ThreadStart(SimpleWork); Thread theThread = new Thread(operation); theThread.Start(); 2 3 4
Több fonál létrehozása ThreadStart operation = new ThreadStart(SimpleWork); for (int x = 1; x <= 5; ++x) { Thread theThread = new Thread(operation); theThread.Start(); }
Több fonalas alkalmazás működése • Fő fonál (main thread) létrehozza a munka fonalakat (workingthread), nyilvántartja azokat, amelyek a kiegészítő munkákat végzik. • Grafikát, vezérlőket csak a fő szálból lehet használni! • Amikor a fő szálnak szüksége van az eredményekre Join minden munka szálra a főszálból!
Fonál prioritás • Highest The highest priority • AboveNormalHigher priority than Normal • Normal The default priority • BelowNormalLower than Normal • Lowest The lowest priority
Fonalak létrehozása adat(ok)al • ThreadStarthelyettParameterizedThreadStartdelegátum használata. • A paraméter Object típusú, cast-olni kell!(as, is operátor) • A paraméter aktuális értékét a Start metódusnak kell adni!theThread.Start("Hello");
Fonál leállítás • Hívjuk a Thread.Abortmetódust! • Ennek hatására a szálban a ThreadAbortException kivétel dobódik! • Vagy elkapjuk, vagy nem, de a fonál leáll!
Kritikus szekció • Az Abort metódus megszakítja a normál futást, ami következtében inkonzistens lehet az alkalmazásunk. • Ezt elkerülendő használjuk a kritikus szakaszt. • Ezt nem szakíthatja meg a rendszert. Thread.BeginCriticalRegion(); SomeClass.IsValid = true; SomeClass.IsComplete = true; Thread.EndCriticalRegion();
Futási környezet - Execution Context • Minden szálhoz tartoznak adatok: • biztonsági, • kultúra, • tranzakció • Elérni a ExecutionContextstatikus metódusaival lehet azokat. • Egy rendszer szintű szál kezeli ezeket az információkat (teljesítmény szükséglet). • Felfüggesztés : AsyncFlowControlflow = ExecutionContext.SuppressFlow() • Visszaállítás: ExecutionContext.RestoreFlow();
Adat megosztás fonalak között • Interlocked • lock{} • Mutex • …
Példakód adat felülírásra public class Counter{ public static int Count; static void UpdateCount() { for (int x = 1; x <= 10000; ++x) { Counter.Count= Counter.Count + 1; } } }
Szálak futtatása ThreadStart starter = new ThreadStart(UpdateCount); Thread[] threads = new Thread[10]; for (int x = 0; x < 10; ++x) { threads[x] = new Thread(starter); threads[x].Start(); } for (int x = 0; x < 10; ++x) { threads[x].Join(); }
Eredmény • Hyper-Threading processzoroknál az eredmény nem 100,000 • Mert az írás, olvasás nem atomi! • Counter.Count++ művelet: • Betöltik a változó értékét egy regiszterbe. • Növelik a regiszter értékét. • Új érték vissza a változóba.
Megoldás Interlocked osztály használata. static void UpdateCount() { for (int x = 1; x <= 10000; ++x) { Interlocked.Increment(ref Counter.Count); } }
2 számláló esete public void UpdateCount() { Interlocked.Increment(ref _count); if (Count % 2 == 0){ Interlocked.Increment(ref _evenCount); } } • Újabb probléma: a 2. számláló rossz lesz • Hiszen ugyanazt a szálat beengedi. • Nem az összetett művelet védett csak a 2 kölün-külön!
lock szekció használata public void UpdateCount() { lock (this) { _count = _count + 1; if (Count % 2 == 0) // An even number { _evenCount = _evenCount + 1; } } }
Monitor osztály (2. mo.) public void UpdateCount(){ Monitor.Enter(this); try{ _count = _count + 1; if (Count % 2 == 0) // An even number { _evenCount = _evenCount + 1; } } finally{ Monitor.Exit(this); } }
Monitor statikus metódusai • Több lehetőség adódik a segítségével! • EnterCreates an exclusive lock on a specified object • ExitReleases an exclusive lock on a specified object • TryEnterAttempts to create an exclusive lock on a specified object;optionally supports a timeout value on acquiring the lock • WaitReleases an exclusive lock, and blocks the current threaduntil it can re-acquire the lock
A szinkronizáció problémája • Problémákat old meg, de újat vezet be:holtpont(deadlock) • 2 erőforrás (1,2) 2 szál (A,B)A lokkolja 1-est és ugyanekkor B lokkolja 2-est • majd ezután kellene A-nak a 2es és B-nek az 1es • Mivel esek már lokkoltak mindkettő a másik által foglalt erőforrásra vár.
Példakód holtpontra class Deadlocker{ object ResourceA = new Object(); object ResourceB = new Object(); } • public void First(){ • lock (ResourceA){ • lock (ResourceB){ • Console.WriteLine("First"); • } • } • } • public void Second(){ • lock (ResourceB){ • lock (ResourceA){ • Console.WriteLine("Second"); • } • } • }
Fonalak indítása Deadlocker deadlock = new Deadlocker(); ThreadStartfirstStart = new ThreadStart(deadlock.First); ThreadStartsecondStart = new ThreadStart(deadlock.Second); Thread first = new Thread(firstStart); Thread second = new Thread(secondStart); first.Start(); second.Start(); first.Join(); second.Join();
Megoldás • Monitor.TryEnteregy timeout érték múlva lemond a lokkolási kéréséről és kilép a saját lock-ból. • Csökkentsük a legkisebbre a lokkolt kódot. Csökkent a lokkolási idő, így a holtpont kialakulásának esélye.
ReaderWriterLock • Különbséget teszt olvasási és írási lock között. • Olvasási beenged többet is, de írásiba csak 1 et. • Ha írják, nem lehet olvasni. • UpgradeToWriterLock, DowngradeFromWriterLock
ReaderWriterLock használata ReaderWriterLockrwLock = new ReaderWriterLock(); int counter = 0; try{ rwLock.AcquireReaderLock(100); rwLock.AcquireWriterLock(1000); try{ Console.WriteLine(counter); Interlocked.Increment(ref counter); } finally{rwLock.ReleaseReaderLock();} } catch (ApplicationException){ Console.WriteLine("Failed to get a Lock"); }
Mutex MutextheMutex = null; try // Try and open the Mutex{ theMutex= Mutex.OpenExisting("MYMUTEX"); } catch (WaitHandleCannotBeOpenedException){ // Cannot open the mutex because it doesn't exist } if (theMutex == null){ theMutex= new Mutex(false, "MYMUTEX"); } //használat if (theMutex.WaitOne(1000, false)) // wait 1 second for lock{ }
Aszinkron metódus hívás byte[] buffer = new byte[100]; string filename = string.Concat(Environment.SystemDirectory, "\\mfc71.pdb"); FileStreamstrm = new FileStream(filename,FileMode.Open, FileAccess.Read, FileShare.Read, 1024,FileOptions.Asynchronous); IAsyncResultresult = strm.BeginRead(buffer, 0, buffer.Length, null, null); // Közben csinálhatunk bármit intnumBytes = strm.EndRead(result);// amikor kell az eredmény strm.Close(); Console.WriteLine("Read {0} Bytes", numBytes); Console.WriteLine(BitConverter.ToString(buffer));