Transcript NET
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 Inne obiekty o własnościach semaforów Inne metody synchronizacji
Synchronizacja
synchronizacja
wątków - szeregowanie w czasie operacji
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)
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
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 LeaveCriticalSection
wejście do sekcji krytycznej wyjście z sekcji krytycznej
TryEnterCriticalSection
sprawdzenie sekcji krytycznej .NET:
Monitor
Sekcja krytyczna
} lock (x) { ....
– C# - lock
} System.Threading.Monitor.Enter(x); try { finally { ...
} System.Threading.Monitor.Exit(x); Kontekst pozwala identyfikować poszczególne instancje sekcji krytycznej Aby skorzystać z konstrukcji typeof np. lock uzyskać globalny kontekst można (typeof(myObject)) synchronizowalne wrapery do zmiennych np.
Hashtable myHT = new Hashtable(); Hashtable mySyncHT = Hashtable.Synchronized(myHT);
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 }
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 buf.
sygnalizowane gdy są jakieś znaki w
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
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
Opuszczanie semafora
opuszczenie pojedynczego semafora DWORD
WaitForSingleObject
dwMiliseconds) ; (HANDLE hObject, DWORD 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 ) ;
};
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
public SafeWaitHandle { get; set; }
SafeWaitHandle
{ get; set; }
Opuszczanie semafora .NET
class
ManualResetEvent
:
WaitHandle
; class
AutoResetEvent
:
WaitHandle
; class
Mutex
:
WaitHandle
; class
Semaphore
:
WaitHandle
;
Event
Zdarzenie
wydarzenia - obiekt dwustanowy, służący do sygnalizowania zajścia Synchronizacja wątków dowolnych procesów Typy zdarzeń manual-reset event
-
podniesienie
wznawia wszystkie wątki
automatycznie opuszczane -
jeden wątek podniesienie wznawia zawsze
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)
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
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
LONG lInitialCount , LONG (PSECURITY_ATRIBUTE
psa
, 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)
.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.
.NET:
Barrier
Barrier
wątki 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 Dennis Mac Charlie Gas Station =
Barrier
Bost on
Barrier - Code
.NET:
CountdownEvent
CountdownEvent
jest obiektem synchronizacyjnym który pozwala śledzić wykonanie wiekszej liczby zadań i sygnalizować ich zakończenie.
Master Thread F O R K Parallel Region J O I N Master Thread
CountdownEvent - Code
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, DWORD fdwFilter) ; // czy zmiana poddrzewa // rodzaj zmiany
FindNextChangeNotification
- zlecenie dokonania kolejnego powiadomienia
FindCloseChangeNotification
- rezygnacja
WaitFor
...
- oczekiwanie na powiadomienie .NET:
System.IO.FileSystemWatcher
Inne metody synchronizacji
WaitForInputIddle wMillisecinds) (HANDLE hProcess, DWORD .NET : System.Diagnostics.Process.WaitForInputIdle
Czeka aż wątek główny przetworzy wszystkie komunikaty (np. emulacja naciśnięć klawiszy)
O mierzeniu czasu
Sleep(x
)
System.Threading.Timer
System.Timers.Timer
(inny wątek via ThreadPool)
(inny wątek) Dedykowane dla okien:
System.Windows.Forms.Timer
(WindowsForms)
System.Windows.Threading.DispatcherTimer
(WPF)
Waitable timer
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 synchronizacja
- podniesienie wznawia wszystkie - podniesiony wznawia tylko jeden wątki wątek operacje na budziku • •
CreateWaitableTimer
(P SECURITY_ATRIBUTE
bInitialOwned
, PCSTR
pszName
)
OpenWaitableTimer
psa
, BOOL • •
SetWaitableTimer
(HANDLE hTimer, const LARGE_INTEGER *pDueTime, LONG lPeriod, PTIMERAPCROUTINE pfnCompletitionRoutine, PVOID pvArgToCompletitionRoutine, BOOL fResume)
CancelWaitableTimer
- zatrzymanie
.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); [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ługiwana przez 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
Zajmowanie zasobów w takiej samej kolejności Użycie WaitAll
Leniwa inicjalizacja
class Foo { Expensive _expensive; public Expensive Expensive { // Lazily instantiate Expensive lock Get { if (_expensive == null) _expensive = new Expensive(); return _expensive; } } } ...
TLS
static ThreadLocal
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); } } ...
WIELOZADANIOWOŚĆ W .NET
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();
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ć
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();
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();
W ątki
Kiedy używać: długie zadania, priorytet inny niż normal zadania foreground
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
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();
Czekanie inaczej
} var threads = new list
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)
Od watków do zadań (2)
Task Scheduler
Global Queue Local Queue Program Thread Task 1 Task 2 Worker Thread 1 Task 3 Task 4 Task 5 Local Queue Worker Thread p
Task - .NET 4.X
Zeby zadanie cykli miało sens – jego długosc > 200-300 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.
Task - .NET 4.X
Uruchamianie: 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
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
Task - kontynuacje
{ var basicTask=new Task
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,
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();
Task - potomny
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
Task – czekaj na wszystkie
} var taskQueue = new Queue
Task.Factory.ContinueWhenAll(taskQueue.ToArray(), completedTasks => { // Do continuation work.} );
Task – czekaj na pierwszy
} var taskQueue = new Queue
Task.Factory.ContinueWhenAny(taskQueue.ToArray(), completedTask => { Console.WriteLine(completedTask.Result); } );
Task –przetwórz po kolei
} var taskQueue = new Queue
while (! taskQueue.IsEmpty) Task
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.
Unified cancelation Model
CancellationTokenSource Cancel() OP1 OP2 OP3
UCM – przykład
System.Collections.Concurrent
• Przy przetwarzaniu wielozadaniowym należy wykorzystywać klasy kolekcji bezpiecznych ("thread-safe") • ConcurrentStack
• System.Collections.Generic
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 .AsParallel() where p.Name == queryInfo.Name && p.State == queryInfo.State && p.Year >= yearStart && p.Year <= yearEnd orderby p.Year ascending select p;
Parallel Static Class
• Gdy instrukcje są niezależne moga być zrównoleglone
StatementA(); StatementB(); StatementC(); Parallel.Invoke( () => StatementA(), () => StatementB(), () => StatementC() );
Parallel.For Przykład
Paralel Invoke
public static void
Invoke (
params Action[] actions); Parallel.Invoke ( () => new WebClient().DownloadFile
("http://www.wp.pl", “index.html"), () => new WebClient().DownloadFile
("http://www.pg.gda.pl", “index.html") ); Np. Cancelation token public static void
Invoke (ParallelOptions options,
params Action[] actions);
Paralel Enumerable
public static void
Invoke (
params Action[] actions); val w = ParallelEnumerable.Range (1, 10000000).Sum ( i => Math.Sqrt (i)) public static void
Invoke (ParallelOptions options,
params Action[] actions);
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 szybko do 100 watkow … poza pulą – prowadzi Optymalizacja: wystartowanie tylu dorzucanie nowych zadań ile jest rdzeni i w WaitAny 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));
Task – kontynuacja po nowemu
{ var basicTask = new Task
Task – completition source
} public class TaskCompletionSource
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); };
completition source – DoAfter
} { Task DoAfter (int msToDelay) var tcs = new TaskCompletionSource
Operacje asynchroniczne
i Podejście wielowątkowe: Tworzymy kod jako synchroniczny 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 wielowątkowej synchronizacji złożonej logiki
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
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 = 1000000; 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));
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 = 1000000; for (int i = 0; i < 10; i++) { // kolejnosc ???
var awaiter = GetPrimesCountAsync (i*1000000 + 2, 1000000).GetAwaiter(); awaiter.OnCompleted (() => Console.WriteLine ( “Znaleziono:” + awaiter.GetResult() + “... "));
Przykład asynchroniczny
} void DisplayPrimeCountsFrom (int i, int count ){ var awaiter = GetPrimesCountAsync (i*1000000 + 2, 1000000).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
Przykład asynchroniczny II
} class PrimesStateMachine { const int MaxCount = 10; } TaskCompletionSource
Przykład asynchroniczny II cd.
} Task DisplayPrimeCountsAsync() { var machine = new PrimesStateMachine(); machine.DisplayPrimeCountsFrom(0); return machine.Task;
Przykład asynchroniczny 5.0
} Task
Przykład asynchroniczny 5.0
{
async
void DisplayPrimesCount() int result =
await
GetPrimesCountAsync (2, 1000000); Console.WriteLine (result); } } Odpowiada nast kodowi void DisplayPrimesCount() { var awaiter = GetPrimesCountAsync (2, 1000000).GetAwaiter(); awaiter.OnCompleted (() => { int result = awaiter.GetResult(); Console.WriteLine (result); });
async
Może być zaaplikowany do metod zwracających void Task Task
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 odpowiedni typ i właściwość IsCompleted) zwracające 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).
Konstrukcja bogatego GUI
Piszemy metody synchronicznie Zamieniamy synchroniczne wykonujemy na nich await wołania na asynchroniczne i Z wyjątkiem głównych metod (obsługa zdarzeń w GUI) zamieniamy typy zwracane na Task lub Task
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();