Kölcsönös kizárás, atomicitás, szemafor kertesz.gabor@nik.uni-obuda.hu
Lock lock (object o) struktúra C#-ban Kölcsönös kizárás megvalósítása object o: szinkronizációs objektum Bármely közös változó lehet, referencia típusúnak kell lennie Egyszerre csak egy szál szerezheti meg Amelyik szálnál az objektum, az tud belépni () a kritikus szakaszba Kilépéskor () a lockot elengedi FIFO várósor: first-come first-serve
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); Figyelem: SOHA ne írjunk le az alábbiakra hasonlító kódot: lock (this) vagy lock (typeof(program)) azaz sose lockoljunk olyat, amiről nem tudjuk befolyásolni, ki fér hozzá 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 Forrás: Haladó Programozás labor anyagai
Reentrant lock Beágyazott lockok ugyanahhoz a szinkronizációs objektumhoz lock (locker) lock (locker) lock (locker) // Do something... Nem blokkolja önmagát Csak a legkülső Exit()-nél engedi el a lockot
Reentrant lock static readonly object _locker = new object(); static void Main() lock (_locker) AnotherMethod(); // We still have the lock - because locks are reentrant. static void AnotherMethod() lock (_locker) Console.WriteLine ("Another method"); Nem blokkolja önmagát Csak a legkülső Exit()-nél engedi el a lockot
Mutex Inter-process megoldás Lassabb mint a lock Mutex class példányain hívható a WaitOne() és a ReleaseMutex() a lock megszerzésére és elengedésére A lockot elengedi a folyamat bezárásakor is
Mutex static void Main() // Naming a Mutex makes it available computer-wide. Use a name that's // unique to your company and application (e.g., include your URL). using (var mutex = new Mutex (false, "oreilly.com OneAtATimeDemo")) // Wait a few seconds if contended, in case another instance // of the program is still in the process of shutting down. if (!mutex.waitone (TimeSpan.FromSeconds (3), false)) Console.WriteLine ("Another instance of the app is running. Bye!"); return; RunProgram(); static void RunProgram() Console.WriteLine ("Running. Press Enter to exit"); Console.ReadLine();
Szemafor Semaphore és SemaphoreSlim (>=.NET 4.0) A Semaphore folyamatok között is alkalmazható, de lassabb static SemaphoreSlim _sem = new SemaphoreSlim (3); static void Main() for (int i = 1; i <= 5; i++) new Thread (Enter).Start (i); static void Enter (object id) Console.WriteLine (id + " wants to enter"); _sem.wait(); Console.WriteLine (id + " is in!"); Thread.Sleep (1000 * (int) id); Console.WriteLine (id + " is leaving"); _sem.release(); 1 wants to enter 1 is in! 2 wants to enter 2 is in! 3 wants to enter 3 is in! 4 wants to enter 5 wants to enter 1 is leaving 4 is in! 2 is leaving 5 is in!
Atomicitás Atomi műveletek az Interlocked class metódusain keresztül Add(rex x, value), Subtract(ref x, value) Increment(ref x), Decrement(ref x) Exchange(ref int x, value) CompareExchange(ref x, int value, int originalvalue) (CompareAndSwap az előadásanyagból)
Monitor Action Enter, TryEnter Wait Pulse (signal), PulseAll Exit Description Acquires a lock for an object. This action also marks the beginning of a critical section. No other thread can enter the critical section unless it is executing the instructions in the critical section using a different locked object. Releases the lock on an object in order to permit other threads to lock and access the object. The calling thread waits while another thread accesses the object. Pulse signals are used to notify waiting threads about changes to an object's state. Sends a signal to one or more waiting threads. The signal notifies a waiting thread that the state of the locked object has changed, and the owner of the lock is ready to release the lock. The waiting thread is placed in the object's ready queue so that it might eventually receive the lock for the object. Once the thread has the lock, it can check the new state of the object to see if the required state has been reached. Releases the lock on an object. This action also marks the end of a critical section protected by the locked object. Forrás: MSDN
Monitor Monitor.Enter & Monitor.Exit gyakorlatilag a lock struktúra Monitor.Enter (_locker); try if (_val2!= 0) Console.WriteLine (_val1 / _val2); _val2 = 0; finally Monitor.Exit (_locker);
Monitor Here s how to use Wait and Pulse: 1. Define a single field for use as the synchronization object, such as: readonly object _locker = new object(); 2. Define field(s) for use in your custom blocking condition(s). For example: bool _go; //or: int _semaphorecount; 3. Whenever you want to block, include the following code: lock (_locker) while ( <blocking-condition> ) Monitor.Wait (_locker); 4. Whenever you change (or potentially change) a blocking condition, include this code: lock (_locker) < alter the field(s) or data that might impact the blocking condition(s) > Monitor.Pulse (_locker); //or: Monitor.PulseAll (_locker)
Feladat #0 4 szál egyidejűleg végez ismétlődő időigényes műveletet (random változó Thread.Sleep() segítségével imitálható), és amint részeredményt kapnak, azt a konzolra írják. Minden szál a konzol egy definiált sorába írhat (1-4), egy előre definiált színnel. Készítsen szálbiztos megoldást! Console.SetCursorPosition(x, y) Console.ForegroundColor = Console.Write( ) b) A részeredményeket menet közben összegezze biztonságosan egy közös változóba!
Feladat #1 Adott N db URL, amelyeken állományokat találunk, ezek együttes letöltése a feladatunk. Nem célszerű egyszerre elindítani N darab párhuzamos letöltést, felső korlát beállítására lehet szükség. Jól jöhet: new WebClient().DownloadFile(URL, PATH); new SemaphoreSlim(initialCount: LIMIT);
Feladat #2 Étkező filozófusok probléma megoldása monitorral az előadásanyag alapján Jól jöhet: Monitor.Enter() Monitor.TryEnter() Monitor.Wait() Monitor.Pulse()
Ajánlott olvasmány Joseph Albahari: Threading in C#, www.albahari.com