810 likes | 969 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ądra • 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.5 – zalecany, pracujehybrydowotj. nieuzywa obj. jądrapókinie jest to konieczne)
.NET: ReaderWriterLock(Slim) • Semafordedykowanydlaasymetrycznejsytuacjigdzeistniejemożliwośćwieludostępów vs. dostępwyłączny • operacjenasemaforzewielowartościowym • 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
.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
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 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
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); } } ... }
Wątki Kiedyużywać:Długiezadania, priorytetinnyniż normal, foreground task • new Thread (() => { } ).Start(); • new Thread (() => { } ).Start(startState); Wynik dla: for (int i = 0; i < 10; i++) new Thread (() => Console.Write (i)).Start();
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 • Uruchamianie: ThreadPool.QueueUserWorkItem (notUsed => Console.WriteLine ("Msg from pool")); • Kiedyużywać: • krótkiezadania (<250ms, idealnie<100ms), • priorytet normal, • zadania background • Używaneprzez : WCF, Remoting, ASP.NET, and ASMX Web Services, System.Timers.TimeriSystem.Threading.Timer, BackgroundWorkerasynchr. delegaty
Wątki, pula Problemy: • Odbieraniewartości z wątku • 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)
Task Scheduler Local Queue Local Queue Global Queue Worker Thread 1 Program Thread Worker Thread p Task 1 Task 3 Task 4 Task 5 Task 2
Task - .NET 4.X • Zebyzadaniemiałosens – jegodługosc > 200-300 cykli • Domyslnie scheduler używaglobalnejkolejki (można to zmienić) • Abyzapewnićnp. priorytetylubniekorzystanie z .Netpuliwątkówkoniczne jest zaimplementowaniewłasnegoschedulera.
Task - .NET 4.X • Uruchamianie: Task task = Task.Run (() => Console.WriteLine (“Task")).Start(); Zadaniasądomyślnierealizowaneprzezpulę • Długiezadania (nieblokująpuli): Task task = Task.Factory.StartNew (() => …, TaskCreationOptions.LongRunning); • Oczekiwanienazakończeniezadania task.Wait(); lub Task<int> task = Task.Run (() => { return 3; }); Int ret = task.Result;
Task vs. wyjątki • Nieobsłużonewyjątkisąprzechowywane (iwyrzucane) • w momencieodczytuzwrotu • lubwykonania Wait nazadaniu • W 4.0 byływyrzucaneprzyfinalizatorze (crash aplikacji) • CLR opakowujewyjątki wAggregateException Task task = Task.Run ( () => { throw new ApplicationException(“Problem”); }); try { task.Wait(); } catch (AggregateExceptionaex) { Console.WriteLine (aex.InnerException.ToString() ); } • Stan zadaniamożnasprawdzićprzezIsFaultediIsCanceled • Sam wyjątek jest dostępnyprzezwłaściwośćException
Task - kontynuacje varbasicTask=new Task<int> (()=> {return 5;} ); basicTask.ContinueWith (antecedent => { int result = antecedent.Result; Console.WriteLine (result); // Writes 123 }); basicTask.Start(); • Zwyklekolejnezadaniawykonuje ten samwątek (dlaguizawsze) • jeslichcemymiećpewność: ContinueWith (antecedent =>{…}, TaskContinuationOptions.ExecuteSynchronously); • Możnazdefiniowaćkilkanastępców. Domyślniezostanąuruchomionewszystkienaraz. ExecuteSynchronously je szerguje. • Inneopcje: NotOnCanceled, OnlyOnCanceled, LazyCancellation, LongRunning, NotOnFaulted,
Task - zagnieżdzony var parent = Task.Factory.StartNew(() => { Console.WriteLine("Outer task executing."); var child = Task.Factory.StartNew(() => { Console.WriteLine("Nested task starting."); Thread.SpinWait(500000); Console.WriteLine("Nested task completing."); }); }); parent.Wait();