Pobieranie prezentacji. Proszę czekać

Pobieranie prezentacji. Proszę czekać

Programowanie Aplikacji Lokalnych w Środowisku .NET

Podobne prezentacje


Prezentacja na temat: "Programowanie Aplikacji Lokalnych w Środowisku .NET"— Zapis prezentacji:

1 Programowanie Aplikacji Lokalnych w Środowisku .NET
Synchronizacja Obiekty synchronizacyjne Pula wątków MS Parallel Computing Initiative Operacje asynchroniczne

2 Synchronizacja Nieprzerywalne operacje Ochrona sekcji krytycznej
Odmiany semaforów Inne obiekty o własnościach semaforów Inne metody synchronizacji

3 Synchronizacja Należy unikać!
synchronizacja - szeregowanie w czasie operacji wątków Należy unikać! aktywnego czekania jako metody synchronizacji (SpinLock, SpinWait?) Uśpienie wątku: Thread.Sleep(TimeSpan) Najprostszy sposób oczekiwania na koniec wątku - Thread.Join(TimeSpan)

4 Synchronizacja w trybie użytkownika
Dużo mniej kosztowna niż wykorzystanie obiektów jądra Możliwe jest synchronizowanie tylko wątków tego samego procesu Funkcje atomowe: InterlockedIncrement, InterlockedDecrement, InterlockedExchancheAdd, InterlockedExchanche, (32bit) InterlockedExchanchePointer, (64bit) InterlockedCompareExchanche .NET - class Interlooked CompareExchange, Decrement, Exchange, Increment

5 Sekcja krytyczna Synchronizacja w trybie użytkownika
Możliwe jest synchronizowanie tylko wątków tego samego procesu Uwaga wykonanie „wait” może spowodować – zmianę trybu Typ obiektu: CRITICAL_SECTION Operacje na sekcji krytycznej: InitializeCriticalSection inicjalizacja sekcji krytycznej DeleteCriticalSection zwolnienie sekcji krytycznej EnterCriticalSection wejście do sekcji krytycznej LeaveCriticalSection wyjście z sekcji krytycznej TryEnterCriticalSection sprawdzenie sekcji krytycznej .NET: Monitor

6 Sekcja krytyczna – C# - lock
lock (x) { .... } System.Threading.Monitor.Enter(x); try { } finally { System.Threading.Monitor.Exit(x); } Kontekst pozwala identyfikować poszczególne instancje sekcji krytycznej Aby uzyskać globalny kontekst można skorzystać z konstrukcji typeof np. lock(typeof(myObject)) synchronizowalne wrapery do zmiennych np. Hashtable myHT = new Hashtable(); Hashtable mySyncHT = Hashtable.Synchronized(myHT);

7 Dostęp do zmiennych Thread Local Storage
LocalDataStoreSlot lds = Thread.GetNamedDataSlot("COOKIE"); Thread.SetData(lds, "abc"); var napis = (string) Thread.GetData(lds);                       Synchronizowalne wrapery do zmiennych np. Hashtable myHT = new Hashtable(); Hashtable mySyncHT = Hashtable.Synchronized(myHT); lock(mySyncHT.SyncRoot) { foreach (Object item in mySyncHT) { // to do smthg }

8 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ście konsoli - sygnalizowane gdy są jakieś znaki w buf.

9 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

10 Obiekt synchronizacyjny
obiekt przyjmujący stany signaled – semafor podniesiony not signaled – semafor opuszczony operacje zasygnalizowanie (podniesienie semafora - funkcje dedykowane dla typu obiektu likwidacja stanu zasygnalizowanego (opuszczenie semafora) – funkcje wspólne dla wszystkich typów obiektów

11 Opuszczanie semafora opuszczenie pojedynczego semafora
DWORD WaitForSingleObject (HANDLE hObject, DWORD dwMiliseconds) ; INFINITE opuszczanie zbioru semaforów DWORD WaitForMultipleObjects (DWORD nCount, CONST HANDLE* lpHandles, BOOL bWaitAll, DWORD dwMiliseconds) Oczekiwanie przerywane komunikatami MsgWaitForMultipleObjects Oczekiwanie przerywane operacjami I/O WaitForSingleObjectEx((HANDLE hObject, DWORD dwMiliseconds, BOOL bAlertable) ; WaitForMultipleObjectsEx, MsgWaitForMultipleObjectsEx SignalObjectAndWait(HANDLE hObjectToSignal, HANDLE hObjectToWait, DWORD dwMiliseconds, BOOL bAlertable) ;

12 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 IntPtr Handle { get; set; } public SafeWaitHandle SafeWaitHandle { get; set; } };

13 Opuszczanie semafora .NET
class ManualResetEvent : WaitHandle; class AutoResetEvent : WaitHandle; class Mutex : WaitHandle; class Semaphore : WaitHandle;

14 Event Zdarzenie - obiekt dwustanowy, służący do sygnalizowania zajścia wydarzenia Synchronizacja wątków dowolnych procesów Typy zdarzeń manual-reset event - podniesienie wznawia wszystkie wątki automatycznie opuszczane - podniesienie wznawia zawsze jeden wątek Operacje na zdarzeniu CreateEvent (PSECURITY_ATRIBUTE psa, BOOL bManual, BOOL bInitial, PCSTR pszName) OpenEvent SetEvent - podniesienie ResetEvent - opuszczenie PulseEvent - podniesienie i opuszczenie - zwalnia wszystkie czekające wątki (manual) lub jeden(auto) .Net: AutoResetEvent, ManualResetEvent AutoResetEventSlim, ManualResetEventSlim (.Net 4.5)

15 Mutex Semafor binarny - obiekt dwustanowy
Umożliwia synchronizację wątków dowolnych procesów Podniesienie semafora binarnego wznawia tylko jeden wątek Mutex jest własnością wątku: wątek nie czeka na zajętym już przez siebie Mutexie, ale trzeba odpowiednią liczbę razy wołać ReleaseMutex) podnieść Mutexa może tylko wątek, który go opuścił (dla innych wątków ReleaseMutex nie daje efektu) zakończenie wątku będącego włąścicielem muteksa podnosi ten semafor -> wynik WaitFor... = WAIT_ABANDONED Operacje na semaforze binarnym CreateMutex (PSECURITY_ATRIBUTE psa, BOOL bInitialOwned, PCSTR pszName) OpenMutex ReleaseMutex podniesienie, opuszczanie semafora: WaitFor... .NET: Mutex

16 Semaphore Semafor wielowartościowy cechuje ograniczona liczba stanów (0 oznacza semafor opuszczony) Próba opuszczenia semafor zmniejsza licznik o 1 Podniesienie semafora zwiększa licznik i wznawia tyle wątków, o ile został zmniejszony licznik Opuszczenie i podniesienie może być wykonane przez różne wątki operacje na semaforze wielowartościowym CreateSemaphore (PSECURITY_ATRIBUTE psa, LONG lInitialCount, LONG lMaximumCount, PCSTR pszName) OpenSemaphore ReleaseSemaphore podniesienie .NET: Semaphore, SemaphoreSlim (>=.Net 4.5 – zalecany, pracuje hybrydowo tj. nie uzywa obj. jądra póki nie jest to konieczne)

17 .NET: ReaderWriterLock(Slim)
Semafor dedykowany dla asymetrycznej sytuacji gdze istnieje możliwość wielu dostępów vs. dostęp wyłączny operacje na semaforze wielowartościowym IsReaderLockHeld, IsWriterLockHeld WriterSeqNum AcquireReaderLock, AcquireWriterLock AnyWritersSince UpgradeToWriterLock, DowngradeFromWriterLock ReleaseReaderLock, ReleaseWriterLock RestoreLock, ReleaseLock.

18 .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 Mac Charlie Dennis Gas Station = Barrier Boston

19 Barrier - Code

20 .NET: CountdownEvent CountdownEvent jest obiektem synchronizacyjnym który pozwala śledzić wykonanie wiekszej liczby zadań i sygnalizować ich zakończenie. FORK JOIN Master Thread Parallel Region

21 CountdownEvent - Code

22 Powiadomienie o zmianie w systemie plików
powiadomienie jest semaforem, który zmienia stan na podniesiony w chwili wystąpienia zmiany w systemie plików zlecenie dokonania pierwszego powiadomienia HANDLE FindFirstChangeNotification( LPTSTR lpszPath, // ścieżka BOOL fWatchSubTree, // czy zmiana poddrzewa DWORD fdwFilter) ; // rodzaj zmiany FindNextChangeNotification - zlecenie dokonania kolejnego powiadomienia FindCloseChangeNotification - rezygnacja WaitFor... - oczekiwanie na powiadomienie .NET: System.IO.FileSystemWatcher

23 Inne metody synchronizacji
WaitForInputIddle (HANDLE hProcess, DWORD wMillisecinds) .NET : System.Diagnostics.Process.WaitForInputIdle Czeka aż wątek główny przetworzy wszystkie komunikaty (np. emulacja naciśnięć klawiszy)

24 O mierzeniu czasu Sleep(x)
System.Threading.Timer (inny wątek via ThreadPool) System.Timers.Timer (inny wątek) Dedykowane dla okien: System.Windows.Forms.Timer (WindowsForms) System.Windows.Threading.DispatcherTimer (WPF) Waitable timer

25 Waitable timer - NT/2K/XP
budzik jest obiektem czasu, który jest sygnalizowany po upływie określonego czasu (jednorazowo lub cyklicznie) typy budzików jednokrotne, cykliczne sposób opuszczania ręczne opuszczane - podniesienie wznawia wszystkie wątki synchronizacja - podniesiony wznawia tylko jeden wątek operacje na budziku CreateWaitableTimer(PSECURITY_ATRIBUTE psa, BOOL bInitialOwned, PCSTR pszName) OpenWaitableTimer SetWaitableTimer (HANDLE hTimer, const LARGE_INTEGER *pDueTime, LONG lPeriod, PTIMERAPCROUTINE pfnCompletitionRoutine, PVOID pvArgToCompletitionRoutine, BOOL fResume) CancelWaitableTimer - zatrzymanie

26 .NET vs API Wołanie funkcji unmanage – wymaga ich zaimportowania: [DllImport("msvcrt.dll")] using System; using System.Runtime.InteropServices; class PlatformInvokeTest { [DllImport("msvcrt.dll")] public static extern int puts(string c); internal static extern int _flushall(); public static void Main() puts("Test"); _flushall(); } MSDN -> Platform Invoke Tutorial

27 System.Threading.Timer
Class Timer { ... public Timer (TimerCallback); public Timer (TimerCallback, Object, TimeSpan, TimeSpan); public Timer.Change (TimeSpan, TimeSpan); }; F.callbacku jest obsługiwana przez thread pool

28 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

29 DeadLock - zapobieganie
Zajmowanie zasobów w takiej samej kolejności Użycie WaitAll

30 Leniwa inicjalizacja class Foo { Expensive _expensive; public Expensive Expensive { // Lazily instantiate Expensive Get { if (_expensive == null) _expensive = new Expensive(); return _expensive; } ... lock

31 TLS static ThreadLocal<int> _x = new ThreadLocal<int> (() => 3); var localRandom = new ThreadLocal<Random> ( () => new Random (Guid.NewGuid().GetHashCode()) );

32 TLS class Test { LocalDataStoreSlot _secSlot = Thread.GetNamedDataSlot ("securityLevel"); int SecurityLevel { get { object data = Thread.GetData (_secSlot); return data == null ? 0 : (int) data; // null == unintl. } set { Thread.SetData (_secSlot, value); } ...

33 Wielozadaniowość w .NET

34 Wątki Kiedy używać: Długie zadania, priorytet inny niż 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();

35 Koszt wątku: Pamięć: Obiekt jądra (1.2 kB) Thread environment block (4/8 kB 32/64b) Stos (tryb użytkownika 1MB) Stos (tryb jądra 12/24kB dla 32/64b) DllMain -> DLL_THREAD_ATTACH/ DLL_THREAD_DETACH Wniosek: wątki kosztują i nie należy ich nadużywać

36 Wątki Wątek CLR == wątek systemowy (jak dotąd) Start nowego wą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();

37 Wątki Wątek CLR == wątek systemowy (jak dotąd) Start nowego wą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 (int i = 0; i < 10; i++) { int temp = i; new Thread (() => Console.Write (temp)).Start(); }

38 Wątki Kiedy używać: długie zadania, priorytet inny niż normal
zadania foreground

39 Pula wątków - .NET Obiekt systemowy: ThreadPool Uruchamianie:
ThreadPool.QueueUserWorkItem ( notUsed => Console.WriteLine ("Msg from pool")); Kiedy używać: krótkie zadania (<250ms, idealnie <100ms), priorytet normal, zadania background Używane przez : WCF, Remoting, ASP.NET, and ASMX Web Services, System.Timers.Timer i System.Threading.Timer, BackgroundWorker asynchr. delegaty

40 Wątki, pula Problemy: Odbieranie wartości z wątku
Odebranie i przekazanie ew. wyjątków Czekanie na wątek (synchronizacja) blokuje inny wątek Czekanie: new Thread (() => Console.Write (“test”)).Start().Join(); lub var endOfWork = new CountdownEvent(10); for (var i=1; i<=10; i++) new Thread (() =>endOfWork.signal()).Start(); endOfWork.wait();

41 Czekanie inaczej var threads = new list<Thread>(); for (var i=1; i<=10; i++) { var t = new Thread (() =>endOfWork.signal()); t.Start(); threads.Add(); } WaitHandle.WaitAny(threads, true); // false ?

42 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)

43 Od watków do zadań (1)

44 Od watków do zadań (2)

45 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

46 Task - .NET 4.X Zeby zadanie miało sens – jego długosc > cykli Domyslnie scheduler używa globalnej kolejki (można to zmienić) Aby zapewnić np. priorytety lub niekorzystanie z .Net puli wątków koniczne jest zaimplementowanie własnego schedulera.

47 Task - .NET 4.X Uruchamianie: Długie zadania (nie blokują puli):
Task task = Task.Run ( () => Console.WriteLine (“Task")).Start(); Zadania są domyślnie realizowane przez pulę Długie zadania (nie blokują puli): Task task = Task.Factory.StartNew ( () => …, TaskCreationOptions.LongRunning); Oczekiwanie na zakończenie zadania task.Wait(); lub Task<int> task = Task.Run (() => { return 3; }); Int ret = task.Result;

48 Task vs. wyjątki Nieobsłużone wyjątki są przechowywane (i wyrzucane)
w momencie odczytu zwrotu lub wykonania Wait na zadaniu W 4.0 były wyrzucane przy finalizatorze (crash aplikacji) CLR opakowuje wyjątki w AggregateException Task task = Task.Run ( () => { throw new ApplicationException(“Problem”); }); try { task.Wait(); } catch (AggregateException aex) { Console.WriteLine (aex.InnerException.ToString() ); } Stan zadania można sprawdzić przez IsFaulted i IsCanceled Sam wyjątek jest dostępny przez właściwość Exception

49 Task - kontynuacje var basicTask=new Task<int> (()=> {return 5;} ); basicTask.ContinueWith (antecedent => { int result = antecedent.Result; Console.WriteLine (result); // Writes 123 }); basicTask.Start(); Zwykle kolejne zadania wykonuje ten sam wątek (dla gui zawsze) jesli chcemy mieć pewność: ContinueWith (antecedent =>{…}, TaskContinuationOptions.ExecuteSynchronously); Można zdefiniować kilka następców. Domyślnie zostaną uruchomione wszystkie na raz. ExecuteSynchronously je szerguje. Inne opcje: NotOnCanceled, OnlyOnCanceled, LazyCancellation, LongRunning, NotOnFaulted,

50 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();

51 Task - potomny Task zagnieżdzony vs potomny (-> AttachedToParent)
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."); }, TaskContinuationOptions. AttachedToParent); }); parent.Wait(); Task zagnieżdzony vs potomny (-> AttachedToParent) Rodzic nie czeka na zakończenie – dla p. czeka – tj nie konczy wykonania przed dziecmi (ale nie robi join!!!) Stan rodzica nie zależy od stanu t. zagnieżdżonego – dla p. zależy Rodzic nie przechwytuje wyjątków – dla p. przechwytuje

52 Task – czekaj na wszystkie
var taskQueue = new Queue<Task>(); for (int i = 0; i < 10; i++) { taskQueue.Enqueue(Task.Factory.StartNew(() => { /* Do work. */ })); } // Perform some work with the tasks when they complete. Task.Factory.ContinueWhenAll(taskQueue.ToArray(), completedTasks => { // Do continuation work.} );

53 Task – czekaj na pierwszy
var taskQueue = new Queue<Task<int>>(); for (int i = 0; i < 10; i++) { taskQueue.Enqueue(Task<int>.Factory.StartNew(() => { /*Do work.*/ })); } // Perform some work with the tasks when they complete. Task.Factory.ContinueWhenAny(taskQueue.ToArray(), completedTask => { Console.WriteLine(completedTask.Result); } );

54 Task –przetwórz po kolei
var taskQueue = new Queue<Task<int>>(); for (int i = 0; i < 10; i++) { taskQueue.Enqueue(Task<int>.Factory.StartNew(() => { /*Do work.*/ })); } // Perform some work with the tasks when they complete. while (! taskQueue.IsEmpty) Task<int>.Factory.ContinueWhenAny(taskQueue.ToArray(), completedTask => { Console.WriteLine(completedTask.Result); taskQueue.Remove(completedTask); } );

55 Anulowanie przetwarzania
Tradycyjne podejście: zabicie watków – Problemy: Wykonanie akcji porzadkujących Jak zabic watki z puli? Ustawienie flagi zakończ przetwarzanie Problemy: Wątek może czekać na obiekcie synchr.

56 Unified cancelation Model
CancellationTokenSource Cancel() OP1 OP2 OP3

57 UCM – przykład

58 System.Collections.Concurrent
Przy przetwarzaniu wielozadaniowym należy wykorzystywać klasy kolekcji bezpiecznych ("thread-safe") ConcurrentStack<T> ConcurrentQueue<T> ConcurrentLinkedList<T> ConcurrentDictionary<TKey,TValue> ConcurrentBag<TKey,TValue> BlockingCollection<T> IProducerConsumerCollection<T> Partitioner, Partitioner<T>, OrderablePartitioner<T> zamiast System.Collections System.Collections.Generic

59 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         orderby p.Year ascending         select p; .AsParallel()

60 Parallel Static Class Gdy instrukcje są niezależne moga być zrównoleglone Parallel.Invoke( () => StatementA(), () => StatementB(), () => StatementC() ); StatementA(); StatementB(); StatementC();

61 Parallel.For - Przykład

62 Paralel Invoke public static void Invoke (params Action[] actions); Parallel.Invoke ( () => new WebClient().DownloadFile (" “index.html"), () => new WebClient().DownloadFile (" “index.html") ); public static void Invoke (ParallelOptions options, params Action[] actions); Np. Cancelation token

63 Paralel Enumerable public static void Invoke (params Action[] actions); val w = ParallelEnumerable.Range (1, ).Sum ( i => Math.Sqrt (i)) public static void Invoke (ParallelOptions options, params Action[] actions);

64 Granice zrównoleglania
Zadania mają sens jeśli są krótkie (nie za krótkie ). 100 dlugich zadan – prowadzi powoli do 100 watków w puli a to jest nieefektywne 100 dlugich zadan z opcja longrunning – prowadzi szybko do 100 watkow… poza pulą Optymalizacja: wystartowanie tylu zadań ile jest rdzeni i w WaitAny dorzucanie nowych zadań. Użycie Parallel loop/for/foreach - w opcjach można określić ogranicznie paralelizmu: ParallelOptions options = new ParallelOptions(); options.MaxDegreeOfParallelism = System.Environment.ProcessorCount; Parallel.For (sourceCollection, options, item => Process(item));

65 Task – kontynuacja po nowemu
var basicTask = new Task<int> (()=> {return 15;} ); var awaiter = basicTask.GetAwaiter(); awaiter.OnCompleted (() => { int result = awaiter. GetResult(); Console.WriteLine (result); // Writes 123 }); basicTask.Start();

66 Task – completition source
public class TaskCompletionSource<TResult> { public void SetResult (TResult result); public void SetException (Exception exception); public void SetCanceled(); public bool TrySetResult (TResult result); public bool TrySetException (Exception exception); public bool TrySetCanceled(); } var tcs = new TaskCompletionSource<int>(); new Thread (() => { int ret = DoSmthg(); tcs.SetResult (ret); }) .Start(); Task<int> task = tcs.Task; // dedykowany, task oczekujacy Console.WriteLine (task.Result); // poczeka i wypisze ret wołana jedna z

67 completition source Może być wykorzystane do czekania np. na I/O Nie koniecznie musi czekać na rezultat z nowego wątku tworzonego ex-plicite Np.: var timer = new System.Timers.Timer (1000) { AutoReset = false }; timer.Elapsed += delegate { timer.Dispose(); tcs.SetResult (10); };

68 completition source – DoAfter
Task DoAfter (int msToDelay) { var tcs = new TaskCompletionSource<object>(); var timer = new System.Timers.Timer (msToDelay) { AutoReset = false }; timer.Elapsed += delegate { timer.Dispose(); tcs.SetResult (null); }; timer.Start(); return tcs.Task; } DoAfter(1000).GetAwaiter() .OnCompleted (() => DoSomethingAfter1s ());

69 Operacje asynchroniczne
Podejście wielowątkowe: Tworzymy kod jako synchroniczny i wywołujemy go w oddzielnym wątku Podejście asynchroniczne: Funkcja może działać jeszcze po zwróceniu sterowania. Dopóki nie próbujemy uzyskać wyniku od operacji działającej istotnie długo – nie ma wpływu na wątek który ja wywołał (model zalecany np. dla metro, SL) Przykładem są np. ContinueWith/OnContinue 2 typowe scenariusze: Po stronie serwerowej duza ilość operacji IO W aplikacji klienckiej uproszenie złożonej logiki wielowątkowej synchronizacji

70 Operacje asynchroniczne
Model zalecany dla np. dla metro, SL Nie startujemy nowych wątków ex-plicite długie operacje (>=50ms) uruchamiamy jako asynchroniczne Krótkie operacje robimy w wątku GUI Zasadniczo można nie robić synchroniazacji

71 Przykład tradycyjny int GetPrimesCount (int start, int count) { return ParallelEnumerable.Range (start, count).Count ( n => Enumerable.Range (2, (int)Math.Sqrt(n)-1).All ( i => n % i > 0)); } void DisplayPrimeCounts() { const int przedzial = ; for (int i = 0; i < 10; i++) Console.WriteLine (" Znaleziono: " + GetPrimesCount (i*przedzial + 2, przedzial) + " liczb pierwszych między " + (i* przedzial) + " oraz " + ((i+1)* przedzial -1));

72 Przykład wielowątkowy
int GetPrimesCountAsync (int start, int count) { return Task.Run ( () => ParallelEnumerable.Range (start, count).Count ( n =>Enumerable.Range (2, (int) Math.Sqrt(n)-1).All ( i => n % i > 0))); } void DisplayPrimeCounts() { const int przedzial = ; for (int i = 0; i < 10; i++) {// kolejnosc ??? var awaiter = GetPrimesCountAsync (i* , ).GetAwaiter(); awaiter.OnCompleted (() => Console.WriteLine ( “Znaleziono:” + awaiter.GetResult() + “... "));

73 Przykład asynchroniczny
void DisplayPrimeCountsFrom (int i, int count ){ var awaiter = GetPrimesCountAsync (i* , ).GetAwaiter(); awaiter.OnCompleted (() => { Console.WriteLine (“Znaleziono:”, awaiter.GetResult()+ "…"); if (count>0) DisplayPrimeCountsFrom (i,count-1); else Console.WriteLine ("Done"); }); } void DisplayPrimeCounts() { DisplayPrimeCountsFrom (0, 10); DisplayPrimeCounts sam nie jest asynchroniczna

74 Przykład asynchroniczny II
class PrimesStateMachine { const int MaxCount = 10; TaskCompletionSource<object> _tcs = new TaskCompletionSource<object>(); public Task Task { get { return _tcs.Task; } } public void DisplayPrimeCountsFrom (int i) { var awaiter = GetPrimesCountAsync (i* , ).GetAwaiter(); awaiter.OnCompleted (() => { Console.WriteLine (awaiter.GetResult()); if (i++ < MaxCount) DisplayPrimeCountsFrom (i); else { Console.WriteLine ("Done"); _tcs.SetResult (null); } }); }

75 Przykład asynchroniczny II cd.
Task DisplayPrimeCountsAsync() { var machine = new PrimesStateMachine(); machine.DisplayPrimeCountsFrom(0); return machine.Task; }

76 Przykład asynchroniczny 5.0
Task<int> GetPrimesCountAsync (int start, int count) { return Task.Run ( () => ParallelEnumerable.Range (start, count).Count ( n =>Enumerable.Range (2, (int)Math.Sqrt(n)-1).All ( i => n % i > 0))); } async void DisplayPrimeCounts() { for (int i = 0; i < 10; i++) Console.WriteLine ( await GetPrimesCountAsync (i* , ));

77 Przykład asynchroniczny 5.0
async void DisplayPrimesCount() { int result = await GetPrimesCountAsync (2, ); Console.WriteLine (result); } Odpowiada nast kodowi void DisplayPrimesCount() { var awaiter = GetPrimesCountAsync (2, ).GetAwaiter(); awaiter.OnCompleted (() => { int result = awaiter.GetResult(); });

78 async Może być zaaplikowany do metod zwracających
void Task Task<T> Nie zmienia sygnatury (podobnie jak unsafe) W ciele metody napotkanie await zwraca sterownie (podobnie jak yield) Async może być dodany do labdy i metod anonimowych

79 await Typowo wywoływany jest na Task-u
Wystarczy aby przedmiot wołania await miał Metodę GetAwaiter która zwróci obiekt implementujący INotifyCompletion.OnCompleted (tj. GetResult zwracające odpowiedni typ i właściwość IsCompleted) Może wystąpic w metodach asynchronicznych praktycznie wszędzie z wyjątkiem: catch / finally Wyrażenia lock, Kontekstu unsafe Punktu wejścia do aplikacji (main method).

80 Konstrukcja bogatego GUI
Piszemy metody synchronicznie Zamieniamy synchroniczne wołania na asynchroniczne i wykonujemy na nich await Z wyjątkiem głównych metod (obsługa zdarzeń w GUI) zamieniamy typy zwracane na Task lub Task<TResult> tak by można było na nich czekać

81 Równoległość ponownie
Wołanie metod async bez await powoduje ich równoległe wykonanie. async Task DoSmthg1() { … } async Task DoSmthg2() var task1 = DoSmthg1(); var task2 = DoSmthg2();


Pobierz ppt "Programowanie Aplikacji Lokalnych w Środowisku .NET"

Podobne prezentacje


Reklamy Google