Jazyk C# (seminář 11) Pavel Procházka KMI 3. prosince 2014
Motivace Dnes už se prakticky nedělají jednojádrové procesory pokud potřebujeme výkon, musíme zapojit všechna jádra Často potřebujeme dělat více věcí soubežně například počítat umělou inteligenci na pozadí a přitom vykreslovat Může ale docházet k interferencím například jedno vlákno se pokouší celistvě číst soubor a přitom jiné vlákno ten soubor modifikuje Z důvodu možných interferencí chceme provádět některé úkony nedělitelně atomicky, k tomu slouží synchronizační primitiva Vlákno je z pohledu C# metoda
Vlákna v C# Namespace System.Threading Třída Thread reprezentuje vlákno Konstruktor třídy Thread akceptuje delegát typu ThreadStart nebo ParameterizedThreadStart Delegát ThreadStart očekává metodu vracející void bez argumentů Delegát ParameterizedThreadStart očekává metodu vracející void s jediným argumentem typu object, lze tak předat libovolný počet argumentů Vlákno se spouští metodou Start() nebo jejím přetížením Start( object o ) (podle typu delegátu) Metoda void Abort() násilně ukončuje vlákno Metoda void Join() čeká na dokončení vlákna, jakmile skončí pokračuje se ve vykonávání programu
Ilustrační příklad jaký výstup očekáváme? public static void PutText(){ for( int i = 0 ; i < 5 ; i++ ){ Console.WriteLine ("bar"); public static void Main () { Thread othread = new Thread (new ThreadStart(PutText)); othread.start (); for( int i = 0 ; i < 5 ; i++ ){ Console.WriteLine ("foo"); othread.join();
Jeden z mnoha možných výstupů Proč to tak je? foo foo foo foo bar bar bar foo bar bar
Synchronizační primitiva v C# mutex Mutex (mutual exclusion) je to v podstatě proměnná typu bool, která říká, hodnota false říká, že můžeme vstoupit, hodnota true říká, že už někdo vstoupil a tudíž se musí čekat, až bude volno Nejčastěji se pomocí mutexu zabraňuje vícerý přístup přístup ke sdílenému prostředku Třída Mutex, konstruktor Mutex() nebo Mutex( bool InitiallyOwned ) Metoda ReleaseMutex () pouští mutex používáme kdykoliv už nepotřebujeme pracovat se sdíleným prostředkem, provede atomicky nastavení mutexu na false Metoda WaitOne() čeká až bude mutex false, jakmile uvidí false atomicky nastaví mutex na true a pokračuje se ve vykonávání kódu
Mutex v praxi jaký/jaké očekáváte výstupy? public static Mutex m1 = new Mutex (); public static void PutText(){ m1.waitone (); for( int i = 0 ; i < 5 ; i++ ){ Console.WriteLine ("bar"); m1.releasemutex (); public static void Main () { Thread othread = new Thread ( new ThreadStart(PutText)); othread.start (); m1.waitone (); for( int i = 0 ; i < 5 ; i++ ){ Console.WriteLine ("foo"); m1.releasemutex (); othread.join();
Výstup příkladu s mutexem 2 možné. Proč? 1. 2. bar foo bar foo bar foo bar foo bar foo foo bar foo bar foo bar foo bar foo bar
Klíčové slovo lock kritická sekce Slouží k provedení části kódu atomicky tzn. všechna ostatní vlákna programu jsou zastavena při pokusu o vstup do kritické sekce Obvykle slouží ke stejnému účelu jako mutex Typické je použití pro reententrantní metody chceme zajistit, aby stejnou metodu bylo možné volat z více vláken (například Console.WriteLine) a nedošlo k interenci Jak jste si mohli všimnout Console.WriteLine nemíchá znaky, ale vypíše atomicky vždy celý řádek Syntaxe je lock( variable ){ BODY
Lock v praxi totéž, co mutex public static object olock = new object (); public static void PutText(){ lock (olock) { for (int i = 0; i < 5; i++) { Console.WriteLine ("bar"); public static void Main () { Thread othread = new Thread (new ThreadStart(PutText)); othread.start (); lock (olock) { for (int i = 0; i < 5; i++) { Console.WriteLine ("foo"); othread.join();
Semafory v C# Semafor je celočíselná proměnná, které limituje počet vláken přistupujících ke sdílenému prostředku Sémantika: vlákno čeká, pokud semafor má hodnotu 0, přestává čekat, jakmile se hodnota zvýší Každé vlákno když se dočká zvýšení semaforu, tak jeho číslo atomicky sníží Jakmile vlákno již nepotřebuje semafor, uvolní jej a dojde k atomickému zvýšení hodnoty o 1 Třída Semaphore, konstruktor Semaphore(int initialcount, int maximalcount) Metoda WaitOne() čeká na zvýšení semaforu na nenulovou hodnotu, jakmile se dočká atomicky sníží semafor o 1 a pokračuje ve vykonávání programu Metoda Release() atomicky inkrementuje semaforu
A ted vy Napište paralelní verzi jednoho z třídících algoritmů, pokuste se o maximální efektivitu Paralelní program srovnejte se sekvenčním na vícejádrovém CPU a ukažte, že paralelní verze je rychlejší