980 likes | 1.17k Views
Programowanie Aplikacji Lokalnych w Środowisku .NET. Synchronizacja Obiekty synchronizacyjne Pula w ątków MS Parallel Computing Initiative Operacje asynchroniczne. Synchronizacja. Nieprzerywalne operacje Ochrona sekcji krytycznej Odmiany semaforów
E N D
ProgramowanieAplikacjiLokalnychw Środowisku .NET Synchronizacja Obiektysynchronizacyjne Pula wątków MS Parallel Computing Initiative Operacjeasynchroniczne
Synchronizacja • Nieprzerywalneoperacje • Ochronasekcjikrytycznej • Odmianysemaforów • Inneobiekty o własnościachsemaforów • Innemetodysynchronizacji
Synchronizacja • synchronizacja - szeregowanie w czasieoperacjiwątków Należyunikać! • aktywnegoczekaniajakometodysynchronizacji (SpinLock, SpinWait?) • Uśpieniewątku: Thread.Sleep(TimeSpan) • Najprostszysposóboczekiwanianakoniecwątku - Thread.Join(TimeSpan)
Synchronizacja w trybie użytkownika • Dużomniejkosztownaniżwykorzystanieobiektówjądralock:Mutex ~ 1:50 • Możliwe jest synchronizowanietylkowątkówtegosamegoprocesu • Funkcjeatomowe: • InterlockedIncrement, • InterlockedDecrement, • InterlockedExchancheAdd, • InterlockedExchanche, (32bit) • InterlockedExchanchePointer, (64bit) • InterlockedCompareExchanche • .NET - class Interlooked • CompareExchange, Decrement, Exchange, Increment
Sekcja krytyczna • Synchronizacja w trybieużytkownika • Możliwe jest synchronizowanietylkowątkówtegosamegoprocesu • Uwagawykonanie „wait” możespowodować – zmianętrybu Typobiektu: CRITICAL_SECTION Operacjenasekcjikrytycznej: • InitializeCriticalSectioninicjalizacjasekcjikrytycznej • DeleteCriticalSectionzwolnieniesekcjikrytycznej • EnterCriticalSectionwejście do sekcjikrytycznej • LeaveCriticalSectionwyjście z sekcjikrytycznej • TryEnterCriticalSectionsprawdzeniesekcjikrytycznej • .NET: Monitor
Sekcjakrytyczna – C# - lock lock (x){ .... } System.Threading.Monitor.Enter(x); try { ... } finally { System.Threading.Monitor.Exit(x); } • Kontekst pozwala identyfikować poszczególne instancje sekcji krytycznej • Abyuzyskaćglobalnykontekstmożnaskorzystać z konstrukcjitypeofnp. lock(typeof(myObject)) synchronizowalnewrapery do zmiennychnp. HashtablemyHT = new Hashtable(); HashtablemySyncHT = Hashtable.Synchronized(myHT);
Dostęp do zmiennych Thread Local Storage LocalDataStoreSlotlds = Thread.GetNamedDataSlot("COOKIE"); Thread.SetData(lds, "abc"); var napis = (string) Thread.GetData(lds); Synchronizowalnewrapery do zmiennychnp. HashtablemyHT = new Hashtable(); HashtablemySyncHT = Hashtable.Synchronized(myHT); lock(mySyncHT.SyncRoot) { foreach (Object item inmySyncHT) { // to do smthg }
Obiekty synchronizacyjne Synchronizacja w trybie jądra jest dużo bardziej kosztowna • Odmiany semaforów • Mutex – semafor binarny • Semaphore – semafo wielowartościowy • Event – zdarzenie • Waitable timer – budzik: NT, 2K, XP • powiadomienie o zmianie w systemie plików • Obiekty, które mają własności semaforów: • proces, wątek - sygnalizowane po zakończeniu • zadanie - sygnalizowane po wyczerpaniu limitu czasu • plik - sygnalizowane gdy nie trwają operacje we/wy • wejściekonsoli - sygnalizowane gdy są jakieś znaki w buf.
Nazwy obiektów synchronizacyjnych • korzystanie z jednego obiektu przez różne procesy • małe i duże litery są rozróżniane • długość nieprzekraczająca _MAX_PATH • znaki dozwolone takie jak dla nazwy pliku (bez \) • obiekty synchronizacyjne dzielą tę samą przestrzeń nazw • przestrzeń nazw dla obiektów synchronizacyjnych jest rozłączna z przestrzenią nazw systemu plików
Obiekt synchronizacyjny • obiektprzyjmującystany • signaled – semaforpodniesiony • not signaled– semaforopuszczony • operacje • zasygnalizowanie (podniesieniesemafora - funkcjededykowanedlatypuobiektu • likwidacjastanuzasygnalizowanego (opuszczeniesemafora) – funkcjewspólnedlawszystkichtypówobiektów
Opuszczanie semafora • opuszczeniepojedynczegosemafora DWORD WaitForSingleObject (HANDLE hObject, DWORD dwMiliseconds) ; INFINITE • opuszczaniezbiorusemaforówDWORD WaitForMultipleObjects(DWORD nCount, CONST HANDLE* lpHandles, BOOL bWaitAll, DWORD dwMiliseconds) • Oczekiwanieprzerywanekomunikatami MsgWaitForMultipleObjects • Oczekiwanieprzerywaneoperacjami I/O WaitForSingleObjectEx((HANDLE hObject, DWORD dwMiliseconds, BOOL bAlertable) ; WaitForMultipleObjectsEx, MsgWaitForMultipleObjectsEx SignalObjectAndWait(HANDLEhObjectToSignal,HANDLE hObjectToWait, DWORD dwMiliseconds, BOOL bAlertable) ;
Opuszczanie semafora .NET class WaitHandle {public virtual Boolean WaitOne();public static Boolean WaitAll(WaitHandle[]);public static Boolean WaitAny(WaitHandle[]);…public virtual Boolean WaitOne(int, bool);public virtual Boolean WaitOne(TimeSpan, bool);public virtual Boolean SignalAndWait (WaitHandle, WaitHandle) public virtual Boolean SignalAndWait (WaitHandle, WaitHandle, TimeSpan, Boolean);public virtual IntPtrHandle { get; set; }public SafeWaitHandleSafeWaitHandle { get; set; } };
Opuszczanie semafora .NET class ManualResetEvent : WaitHandle; class AutoResetEvent : WaitHandle; class Mutex : WaitHandle; class Semaphore : WaitHandle;
Event • Zdarzenie - obiektdwustanowy, służący do sygnalizowaniazajściawydarzenia • Synchronizacjawątkówdowolnychprocesów • Typyzdarzeń • manual-reset event -podniesieniewznawiawszystkiewątki • automatycznieopuszczane - podniesieniewznawiazawszejedenwątek • Operacjenazdarzeniu • CreateEvent(PSECURITY_ATRIBUTE psa, BOOL bManual, BOOL bInitial, PCSTR pszName) • OpenEvent • SetEvent - podniesienie • ResetEvent - opuszczenie • PulseEvent - podniesienieiopuszczenie - zwalniawszystkieczekającewątki (manual) lubjeden(auto) • .Net: AutoResetEvent, ManualResetEventAutoResetEventSlim, ManualResetEventSlim (.Net 4.5)
Mutex • Semaforbinarny - obiektdwustanowy • Umożliwiasynchronizacjęwątkówdowolnychprocesów • Podniesieniesemaforabinarnegowznawiatylkojedenwątek • Mutex jest własnościąwątku: • wąteknieczekanazajętymjużprzezsiebieMutexie, ale trzebaodpowiedniąliczbęrazywołaćReleaseMutex) • podnieśćMutexamożetylkowątek, który go opuścił (dlainnychwątkówReleaseMutexniedajeefektu) • zakończeniewątkubędącegowłąścicielemmuteksapodnosi ten semafor -> wynikWaitFor... = WAIT_ABANDONED • Operacjenasemaforzebinarnym • CreateMutex (PSECURITY_ATRIBUTE psa, BOOL bInitialOwned, PCSTR pszName) • OpenMutex • ReleaseMutexpodniesienie, opuszczaniesemafora: WaitFor... • .NET:Mutex
Semaphore • Semaforwielowartościowycechujeograniczonaliczbastanów (0 oznaczasemaforopuszczony) • Próbaopuszczeniasemaforzmniejszalicznik o 1 • Podniesieniesemaforazwiększalicznikiwznawiatylewątków, o ilezostałzmniejszonylicznik • Opuszczenieipodniesieniemożebyćwykonaneprzezróżnewątki • operacjenasemaforzewielowartościowym • CreateSemaphore(PSECURITY_ATRIBUTE psa, LONGlInitialCount, LONGlMaximumCount, PCSTR pszName) • OpenSemaphore • ReleaseSemaphorepodniesienie • .NET: Semaphore, SemaphoreSlim(>=.Net 4.0 – zalecany, pracujehybrydowotj. nieuzywa obj. jądrapókinie jest to konieczne)
.NET: xxxSlim • Obiektypracujace w obrębiejednegoprocesu • SemaphoreSlim (5x(?) mniejszynarzut) • ReaderWriterLockzastąpionyprzezReaderWriterLockSlim
.NET: ReaderWriterLockSlim • Semafordedykowanydlaasymetrycznejsytuacjigdzeistniejemożliwośćwieluodczytówvs. dostępwyłączny • operacjenasemaforze • IsReaderLockHeld, IsWriterLockHeld • WriterSeqNum • AcquireReaderLock, AcquireWriterLock • AnyWritersSince • UpgradeToWriterLock, DowngradeFromWriterLock • ReleaseReaderLock, ReleaseWriterLock • RestoreLock, ReleaseLock.
.NET: Barrier Barrier jest obiektem synchronizacyjnym, który pozwala na zatrzymanie wykonania większej liczby wątków w określonym punkcie dopóki nie zostanie on osiagnięty przez wszystkie wątki Dennis Mac Charlie Gas Station = Barrier Boston
Barrier - kod var delay = TimeSpan.FromSeconds(1); varcharlie = new Thread(() => DriveToSeattle("Charlie“, delay)); charlie.Start(); varmac = new Thread(() => DriveToSeattle("Mac", delay)); mac.Start(); vardennis = new Thread(() => DriveToSeattle("Dennis", delay)); dennis.Start(); charlie.Join(); mac.Join(); dennis.Join();
Barrier - kod static void DriveToSeattle(string name, TimeSpantimeToGasStation) { // Drive to gas station Console.WriteLine("[{0}] Leaving House", name); Thread.Sleep(timeToGasStation); Console.WriteLine("[{0}] Arrived at Gas Station", name); // Need to sync here // Perform some more work Console.WriteLine("[{0}] Leaving for Seattle", name); }
Barrier - kod static Barrier sync = new Barrier(3); static void DriveToSeattle(string name, TimeSpantimeToGasStation) { // Drive to gas station Console.WriteLine("[{0}] Leaving House", name); Thread.Sleep(timeToGasStation); Console.WriteLine("[{0}] Arrived at Gas Station", name); // Need to sync here sync .SignalAndWait(); // Perform some more work Console.WriteLine("[{0}] Leaving for Seattle", name); }
.NET: CountdownEvent CountdownEventjest obiektem synchronizacyjnym który pozwala śledzić wykonanie wiekszej liczby zadań i sygnalizować ich zakończenie. JOIN FORK Master Thread Master Thread Parallel Region
CountdownEvent - Kod public static void DoShoping(int id) { Thread.SpinWait(2000000); Console.WriteLine("Customer {0} finished",id); } varsyncEvent = new CountdownEvent(1); foreach (int id in Enumerable.Range(1,20)) { intcurrentId = id; syncEvent.AddCount(); ThreadPool.QueueUserWorkItem(delegate { DoShoping(currentId); syncEvent.Signal(); }); } syncEvent.Signal(); syncEvent.Wait(); Console.WriteLine("All customers finished Shopping");
Powiadomienie o zmianie w systemieplików • powiadomienie jest semaforem, któryzmieniastannapodniesiony w chwiliwystąpieniazmiany w systemieplików • zleceniedokonaniapierwszegopowiadomienia HANDLE FindFirstChangeNotification( LPTSTR lpszPath, // ścieżka BOOL fWatchSubTree, // czyzmianapoddrzewa DWORD fdwFilter) ; // rodzajzmiany • FindNextChangeNotification - zleceniedokonaniakolejnegopowiadomienia • FindCloseChangeNotification - rezygnacja • WaitFor... - oczekiwanienapowiadomienie • .NET: System.IO.FileSystemWatcher
Inne metody synchronizacji • WaitForInputIddle(HANDLE hProcess, DWORD wMillisecinds) • .NET : System.Diagnostics.Process.WaitForInputIdle Czekaażwątekgłównyprzetworzywszystkiekomunikaty (np. emulacjanaciśnięćklawiszy)
O mierzeniuczasu • Sleep(x) • System.Threading.Timer(inny wątek via ThreadPool) • System.Timers.Timer(inny wątek) • Dedykowanedlaokien: • System.Windows.Forms.Timer(WindowsForms) • System.Windows.Threading.DispatcherTimer (WPF) • Waitable timer
Waitable timer - NT/2K/XP • budzik jest obiektemczasu, który jest sygnalizowanypoupływieokreślonegoczasu (jednorazowolubcyklicznie) • typybudzików • jednokrotne, cykliczne • sposóbopuszczania • ręczneopuszczane- podniesieniewznawiawszystkiewątki • synchronizacja - podniesionywznawiatylkojedenwątek • operacjenabudziku • CreateWaitableTimer(PSECURITY_ATRIBUTEpsa, BOOL bInitialOwned, PCSTR pszName) • OpenWaitableTimer • SetWaitableTimer (HANDLE hTimer, const LARGE_INTEGER *pDueTime, LONG lPeriod, PTIMERAPCROUTINE pfnCompletitionRoutine, PVOID pvArgToCompletitionRoutine, BOOL fResume) • CancelWaitableTimer - zatrzymanie • .Net 4.5 ~ Task.Delay(milliseconds)
.NET vs API • Wołaniefunkcjiunmanage – wymagaichzaimportowania: [DllImport("msvcrt.dll")] using System; using System.Runtime.InteropServices; class PlatformInvokeTest { [DllImport("msvcrt.dll")] public static extern intputs(string c); [DllImport("msvcrt.dll")] internal static extern int_flushall(); public static void Main() { puts("Test"); _flushall(); } } MSDN -> Platform Invoke Tutorial
System.Threading.Timer Class Timer { ... public Timer (TimerCallback); public Timer (TimerCallback, Object, TimeSpan, TimeSpan); public Timer.Change (TimeSpan, TimeSpan); ... }; • F.callbacku jest obsługiwanaprzez thread pool
.NetWaitableTimer .Net 4.5 Task.Delay(milliseconds) .Net 4.0
DeadLock object l1 = new object(); object l2 = new object(); new Thread (() => { lock (l1) { Thread.Sleep (1000); lock (l2); // Deadlock } }).Start(); lock (l2) { Thread.Sleep (1000); lock (l1); // Deadlock }
DeadLock - zapobieganie • Zajmowaniezasobów w takiejsamejkolejności • UżycieWaitAll
Leniwainicjalizacja class Foo { Expensive _expensive; public Expensive Expensive { // Lazily instantiate Expensive Get { if (_expensive == null) _expensive = new Expensive(); return _expensive; } } ... } lock
TLS static ThreadLocal<int> _x = new ThreadLocal<int> (() => 3); varlocalRandom = new ThreadLocal<Random>( () => new Random (Guid.NewGuid().GetHashCode()) );
TLS class Test { LocalDataStoreSlot _secSlot = Thread.GetNamedDataSlot ("securityLevel"); intSecurityLevel { get { object data = Thread.GetData (_secSlot); return data == null ? 0 : (int) data; // null == unintl. } set { Thread.SetData (_secSlot, value); } } ... }
Kosztwątku: • Pamięć: • Obiektjądra (1.2 kB) • Thread environment block (4/8 kB 32/64b) • Stos (trybużytkownika 1MB) • Stos (trybjądra 12/24kB dla 32/64b) • DllMain -> DLL_THREAD_ATTACH/ DLL_THREAD_DETACH • Wniosek: wątkikosztująinienależyichnadużywać
Wątki Wątek CLR == wąteksystemowy(jakdotąd) Start nowegowątku: • new Thread (() => { } ).Start(); • new Thread (() => { } ).Start(startState); Jaki będzie wynik dla: for (int i = 0; i < 10; i++) new Thread (() => Console.Write (i)).Start();
Wątki Wątek CLR == wąteksystemowy(jakdotąd) Start nowegowątku: • new Thread (() => { } ).Start(); • new Thread (() => { } ).Start(startState); Jaki będzie wynik dla: for (int i = 0; i < 10; i++) new Thread (() => Console.Write (i)).Start(); a dla for (inti = 0; i < 10; i++) { int temp = i; new Thread (() => Console.Write (temp)).Start(); }
Wątki Kiedyużywać: • długiezadania, • priorytetinnyniż normal • zadania foreground
Pula wątków - .NET • Obiektsystemowy: ThreadPool • Uruchamianiedla void: ThreadPool.QueueUserWorkItem (notUsed => Console.WriteLine ("Msg from pool")); • lubdlazwrotu (alternatywniemożnazdef. f. callb.) static int Work (string s) { return s.Length; } … Func<string, int> method = Work; IAsyncResult cookie = method.BeginInvoke ("test", null, null); … int result = method.EndInvoke (cookie); Console.WriteLine (“Result: " + result);
Pula wątków - .NET • Kiedyużywać: • krótkiezadania (<250ms, idealnie<100ms), • priorytet normal, • zadania background • ThreadPool.Set[Max|Min]Thread nowewątkisąalokowaneod Min do Max jeśliprzezjakiśczas (0.5s) niezmieniasiękolejkazadań do wykonania. • Używaneprzez : WCF, Remoting, ASP.NET, and ASMX Web Services, System.Timers.TimeriSystem.Threading.Timer, BackgroundWorkerasynchr. delegaty
Wątki, pula Pot. problemy: • Odebranieiprzekazanieew. wyjątków • Czekanienawątek (synchronizacja) blokujeinnywątek • Czekanie: new Thread (() => Console.Write (“test”)).Start().Join(); • lub varendOfWork = new CountdownEvent(10); for (vari=1; i<=10; i++) new Thread (() =>endOfWork.signal()).Start(); endOfWork.wait();
Czekanieinaczej var threads = new list<Thread>(); for (vari=1; i<=10; i++) { var t = new Thread (() =>endOfWork.signal()); t.Start(); threads.Add(); } WaitHandle.WaitAny(threads, true); // false ?
Parallel Extensions .Net 4.x • .NET Library • Mogą być wykorzystywane we wszystkich językach na platformie .NET • Obejmuje 3 różne elementy • Parallel LINQ (PLINQ) • Task Parallel Library (TPL) • Coordination Data Structures (CDS)
Od watków do zadań (1) static void Main() {Tree tr = Tree.CreateSomeTree(9, 1);Stopwatch sw = Stopwatch.StartNew();WalkTree(tr);Console.WriteLine("Elapsed= " + sw.ElapsedMilliseconds.ToString());Console.ReadLine(); } static void WalkTree(Tree tree) { if (tree == null) return; WalkTree(tree.Left); WalkTree(tree.Righ); ProcessItem(tree.Data); }
Od watków do zadań (2) static void WalkTree(Tree tree) { if (tree == null) return; Thread left = new Thread((o) => WalkTree(tree.Left)); left.Start(); Thread righ = new Thread((o) => WalkTree(tree.Righ)); righ.Start(); left.Join(); righ.Join(); ProcessItem(tree.Data); }
Od watków do zadań (3) static void WalkTree(Tree tree) { if (tree == null) return; Task left = new Task((o) => WalkTree(tree.Left)); left.Start(); Task righ = new Task((o) => WalkTree(tree.Righ)); righ.Start(); left.Wait(); righ.Wait(); ProcessItem(tree.Data); }
Parallel LINQ-to-Objects (PLINQ) • Wykorzystuje model "Task" • Pozwala na wykorzystanie wielu rdzeni przy zapytaniach LINQ • Wspiera w pełni standardowe operatory zapytań • Minimalizuje wpływ na istniejące zapytania LINQ var q = from p in people where p.Name == queryInfo.Name && p.State == queryInfo.State && p.Year >= yearStart && p.Year <= yearEnd orderbyp.Year ascending select p; .AsParallel()