Programowanie Aplikacji Lokalnych w Środowisku .NET

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

description

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 - PowerPoint PPT Presentation

Transcript of Programowanie Aplikacji Lokalnych w Środowisku .NET

Page 1: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Programowanie Aplikacji Lokalnych w Środowisku .NETSynchronizacja

Obiekty synchronizacyjne

Pula wątków

MS Parallel Computing Initiative Operacje asynchroniczne

Page 2: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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

Page 3: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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

Page 4: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Synchronizacja w trybie użytkownika Dużo mniej kosztowna niż wykorzystanie obiektów jądra

lock:Mutex ~ 1:50 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

Page 5: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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

Page 6: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Sekcja krytyczna – C# - lock

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

System.Threading.Monitor.Enter(x);try { ... }finally { System.Threading.Monitor.Exit(x);}

lock (x) { ....}

Page 7: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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 }

Page 8: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Obiekty synchronizacyjneSynchronizacja 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.

Page 9: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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

Page 10: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Obiekt synchronizacyjny obiekt przyjmujący stany

signaled – semafor podniesionynot signaled – semafor opuszczony

operacjezasygnalizowanie (podniesienie semafora -

funkcje dedykowane dla typu obiektu likwidacja stanu zasygnalizowanego

(opuszczenie semafora) – funkcje wspólne dla wszystkich typów obiektów

Page 11: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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 komunikatamiMsgWaitForMultipleObjects

Oczekiwanie przerywane operacjami I/OWaitForSingleObjectEx((HANDLE hObject, DWORD dwMiliseconds,

BOOL bAlertable) ;WaitForMultipleObjectsEx, MsgWaitForMultipleObjectsExSignalObjectAndWait(HANDLE hObjectToSignal,

HANDLE hObjectToWait, DWORD dwMiliseconds, BOOL bAlertable) ;

Page 12: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Opuszczanie semafora .NETclass 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; }

};

Page 13: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Opuszczanie semafora .NETclass ManualResetEvent : WaitHandle;

class AutoResetEvent : WaitHandle;

class Mutex : WaitHandle;

class Semaphore : WaitHandle;

Page 14: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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)

Page 15: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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

Page 16: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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.0 – zalecany, pracuje hybrydowo tj. nie uzywa obj. jądra póki nie jest to konieczne)

Page 17: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

.NET: xxxSlim Obiekty pracujace w obrębie jednego

procesu SemaphoreSlim (5x(?) mniejszy narzut) ReaderWriterLock zastąpiony przez

ReaderWriterLockSlim

Page 18: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

.NET: ReaderWriterLockSlim Semafor dedykowany dla asymetrycznej

sytuacji gdze istnieje możliwość wielu odczytów vs. dostęp wyłączny

operacje na semaforze• IsReaderLockHeld, IsWriterLockHeld• WriterSeqNum• AcquireReaderLock, AcquireWriterLock• AnyWritersSince• UpgradeToWriterLock, DowngradeFromWriterLock• ReleaseReaderLock, ReleaseWriterLock• RestoreLock, ReleaseLock.

Page 19: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

.NET: Barrier

Mac

Charlie

Dennis

Gas Station = BarrierBoston

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

Page 20: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Barrier - kodvar delay = TimeSpan.FromSeconds(1);

var charlie = new Thread(() => DriveToSeattle("Charlie“, delay));

charlie.Start();

var mac = new Thread(() => DriveToSeattle("Mac", delay));

mac.Start();

var dennis = new Thread(() => DriveToSeattle("Dennis", delay));

dennis.Start();

charlie.Join();

mac.Join();

dennis.Join();

Page 21: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Barrier - kod

static void DriveToSeattle(string name, TimeSpan timeToGasStation)

{

// 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);

}

Page 22: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Barrier - kodstatic Barrier sync = new Barrier(3);

static void DriveToSeattle(string name, TimeSpan timeToGasStation)

{

// 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);

}

Page 23: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

.NET: CountdownEvent

FORK JOINMaster Thread

Parallel Region

Master Thread

CountdownEvent jest obiektem synchronizacyjnym który pozwala śledzić wykonanie wiekszej liczby zadań i sygnalizować ich zakończenie.

Page 24: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

CountdownEvent - Kod public static void DoShoping(int id) {

Thread.SpinWait(2000000);

Console.WriteLine("Customer {0} finished",id);

}

var syncEvent = new CountdownEvent(1);

foreach (int id in Enumerable.Range(1,20)) {

int currentId = id;

syncEvent.AddCount();

ThreadPool.QueueUserWorkItem(delegate {

DoShoping(currentId);

syncEvent.Signal(); });

}

syncEvent.Signal();

syncEvent.Wait();

Console.WriteLine("All customers finished Shopping");

Page 25: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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 poddrzewaDWORD fdwFilter) ; // rodzaj zmiany

FindNextChangeNotification - zlecenie dokonania kolejnego powiadomienia

FindCloseChangeNotification - rezygnacja WaitFor... - oczekiwanie na powiadomienie .NET: System.IO.FileSystemWatcher

Page 26: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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)

Page 27: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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

Page 28: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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 .Net 4.5 ~ Task.Delay(milliseconds)

Page 29: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

.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

Page 30: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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

Page 31: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

.Net WaitableTimer.Net 4.5 Task.Delay(milliseconds).Net 4.0

Page 32: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

DeadLockobject 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

}

Page 33: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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

Page 34: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Leniwa inicjalizacjaclass Foo {

Expensive _expensive;public Expensive expensive { // Lazily instantiate

Expensiveget {

if (_expensive == null) _expensive = new Expensive();return _expensive;

} }

...}

lock

Page 35: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

TLSstatic ThreadLocal<int> _x = new ThreadLocal<int> (()

=> 3);

var localRandom = new ThreadLocal<Random>( () => new Random (Guid.NewGuid().GetHashCode()) );

Page 36: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

TLSclass 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); } }...

}

Page 37: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

WIELOZADANIOWOŚĆ W .NET

Page 38: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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ć

Page 39: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

WątkiWą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();

Page 40: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

WątkiWą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();

}

Page 41: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

WątkiKiedy używać:

długie zadania, priorytet inny niż normal zadania foreground

Page 42: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Pula wątków - .NET Obiekt systemowy: ThreadPool Uruchamianie dla void:

ThreadPool.QueueUserWorkItem (notUsed => Console.WriteLine ("Msg from pool"));

lub dla zwrotu (alternatywnie można zdef. 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);

Page 43: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Pula wątków - .NET Kiedy używać:

krótkie zadania (<250ms, idealnie <100ms), priorytet normal, zadania background ThreadPool.Set[Max|Min]Thread nowe wątki są

alokowane od Min do Max jeśli przez jakiś czas (0.5s) nie zmienia się kolejka zadań do wykonania.

Używane przez : WCF, Remoting, ASP.NET, and ASMX Web Services, System.Timers.Timer i System.Threading.Timer, BackgroundWorker asynchr. delegaty

Page 44: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Wątki, pulaPot. problemy:

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

Page 45: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Czekanie inaczejvar 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 ?

Page 46: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Parallel Extensions .Net 4.x .NET Library

Mogą być wykorzystywane we wszystkich językach na platformie .NET

Obejmuje 3 różne elementyParallel LINQ (PLINQ)Task Parallel Library (TPL)Coordination Data Structures (CDS)

Page 47: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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

}

Page 48: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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); }

Page 49: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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); }

Page 50: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Parallel LINQ-to-Objects

• 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()

(PLINQ)

Page 51: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

PLINQIEnumerable<int> numbers =

Enumerable.Range (3, 100000-3);

var parallelQuery = from n in numbers.AsParallel()

where Enumerable.Range (2, (int) Math.Sqrt (n)).All (i => n % i > 0)

select n;

int[] primes = parallelQuery.ToArray();

Dla operatorów akceptujących 2 sekwencje (Join, GroupJoin, Concat, Union, Intersect, Except, Zip) AsParalel musi być zaaplikowany do obu sekwencji

Page 52: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

PLINQ Warto stosować:

Dla relatywnie prostych przypadków W przypadku. Bardziej skomplikowanych rozwiazań

to podejście może być mniej czytelne

Page 53: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Parallel Static Class

• Gdy instrukcje są niezależne moga być zrównoleglone

StatementA();StatementB();StatementC();

Parallel.Invoke( () => StatementA(), () => StatementB(), () => StatementC() );

Page 54: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Parallel.For - Przykładvoid NonParalelMethod() {

for (int i=0; i<16; i++) {Console.WriteLine(“TID={0}, i={1}”,

Thread.CurrentThread.ManagedThreadId, i);

SimulateProcessing(i);

}

}

void ParalelMethod() {Paralel.For (0, 16, i => {

Console.WriteLine(“TID={0}, i={1}”,Thread.CurrentThread.ManagedThreadId,

i);SimulateProcessing(i);

}

}

Page 55: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Paralel Invokepublic 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")

);

public static void Invoke (ParallelOptions options,params Action[] actions);

Np. Cancelation token

Page 56: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Paralel Enumerablepublic 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);

Page 57: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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

Page 58: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Task Scheduler

Global Queue

Local Queue

Local Queue

Worker

Thread 1

Worker Thread

p

Program Thread

Task 1Task 2

Task 3Task 5Task 4

Page 59: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Task - .NET 4.X Zeby zadanie miało sens – jego długosc powinna

być > 200-300 cykli procesora Domyslnie scheduler używa globalnej puli (można

to zmienić) Aby zapewnić np. priorytety lub niekorzystanie

z .Net puli wątków konieczne jest zaimplementowanie własnego schedulera.

Page 60: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Task - .NET 4.X Uruchamianie:Task task = Task.Run (() => Console.WriteLine (“Task"))

.Start();

Task<int> taskInt = Task.Run (() => { return 3; });

Task task = Task.Factory.StartNew (() => {}, options);

Czekanie:task.Wait();

lub

int ret = taskInt.Result;

Page 61: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Task - .NET 4.X Długie zadania (aby nie blokować puli):Task task = Task.Factory.StartNew (

() => …, TaskCreationOptions.LongRunning);

Opcje: LongRunning PreferFairness – probuje utrzymac kolejnosc wykonania

odpowiadajaca kolejnosci tworzenia AttachedToParent – task potomny DenyChildAttach – rzuca wyjatek przy probie definicji

potomnego task-u HideScheduler – używa domyslnego schedulera dla

StartNew, ContinueWith

Page 62: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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

Page 63: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Task - kontynuacjevar basicTask=new Task<int> (()=> {return 5;} );

basicTask.ContinueWith (antecedent =>

{

int result = antecedent.Result;

Console.WriteLine (result); // Writes 5

});

basicTask.Start();

Page 64: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Task - kontynuacjeTask.Factory.StartNew<int> (() => 8)

.ContinueWith (ant => ant.Result * 2)

.ContinueWith (ant => Math.Sqrt (ant.Result))

.ContinueWith (ant => Console.WriteLine (ant.Result));

Wynik: 256

Page 65: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Task - kontynuacje 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,

Page 66: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Task - zagnieżdzonyvar 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();

Page 67: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Task - potomnyvar parent = Task.Factory.StartNew(() => {

Console.WriteLine("Outer, parent task executing.");

var child = Task.Factory.StartNew(() => {

Console.WriteLine("Nested task starting.");

Thread.SpinWait(500000);

Console.WriteLine("Nested, child task completing.");

}, TaskContinuationOptions. AttachedToParent);

});

parent.Wait();

Page 68: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

T. – zagnieżdzony vs. potomny

Task.Run – blokuje AttachedToParent tak jak opcja DenyChildAttach

Task.Factory.StartNew – uwzględnia AttachedToParent

Nested AttachedToParent

Rodzic nie czeka na zakończenie

Rodzic czeka, tj. nie kończy wykonania przed dziecmi (ale nie robi join!!!)

Stan rodzica nie zależy od stanu tasku

Stan rodzica zależy od stanu tasku

Rodzic nie przechwytuje wyjątków

Rodzic przechwytuje wyjątki

Page 69: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

T. – Cancellation and child tasks

Task cancellation is cooperative. That is, to be cancelable, every attached or detached child task must monitor the status of the cancellation token. If you want to cancel a parent and all its children by using one cancellation request, you pass the same token as an argument to all tasks and provide in each task the logic to respond to the request in each task. For more information, see Task Cancellation and How to: Cancel a Task and Its Children.

When the parent cancels

If a parent cancels itself before its child task is started, the child never starts. If a parent cancels itself after its child task has already started, the child runs to completion unless it has its own cancellation logic. For more information, see Task Cancellation.

When a detached child task cancels

If a detached child task cancels itself by using the same token that was passed to the parent, and the parent does not wait for the child task, no exception is propagated, because the exception is treated as benign cooperation cancellation. This behavior is the same as that of any top-level task.

When an attached child task cancels

When an attached child task cancels itself by using the same token that was passed to its parent task, a TaskCanceledException is propagated to the joining thread inside an AggregateException. You must wait for the parent task so that you can handle all benign exceptions in addition to all faulting exceptions that are propagated up through a graph of attached child tasks.

For more information, see Exception Handling (Task Parallel Library).

Page 70: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Task – czekaj na wszystkievar 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.}

);

Page 71: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Task – czekaj na pierwszyvar 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); }

);

Page 72: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Realizacja przetwórz po koleivar taskQueue = new Queue<Task<int>>();

for (int i = 0; i < 10; i++) {

taskQueue.Enqueue(Task<int>.Factory.StartNew(() => { /*Do work.*/ }));

}

// Wykonaj zbieranie gotowych wyników .

while (! taskQueue.IsEmpty)

Task<int>.Factory.ContinueWhenAny(taskQueue.ToArray(),

completedTask => {

Console.WriteLine(completedTask.Result);

taskQueue.Remove(completedTask); } );

Page 73: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Anulowanie przetwarzaniaTradycyjne podejście: zabicie watków –

Problemy:Wykonanie akcji porzadkującychJak zabić wątki z puli?

Ustawienie flagi zakończ przetwarzanieProblemy:Wątek może czekać na obiekcie synchr.

Page 74: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Unified cancelation Model

CancellationTokenSource

OP2 OP3OP1

Cancel()

Page 75: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Barrier - kodstatic Barrier sync = new Barrier(3);

static CancellationToken token;

static void DriveToSeattle(string name, TimeSpan timeToGasStation)

{

try { // Drive to gas station

Console.WriteLine("[{0}] Leaving House", name);

Thread.Sleep(timeToGasStation);

Console.WriteLine("[{0}] Arrived at Gas Station", name);

sync.SignalAndWait(token);

Console.WriteLine("[{0}] Leaving for Seattle", name);

catch(OperationCancelledException) {

Console.WriteLine("[{0}] Caravan was canceled", name);

}

}

Page 76: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Barrier - kodvar source = new CancellationTokenSource();

token = source.Token;

var delay = TimeSpan.FromSeconds(1);

var charlie = new Thread(() => DriveToSeattle("Charlie“, delay));

charlie.Start();

var mac = new Thread(() => DriveToSeattle("Mac", delay));

mac.Start();

var dennis = new Thread(() => DriveToSeattle("Dennis", delay));

dennis.Start();

source.Cancel();

charlie.Join();

mac.Join();

dennis.Join();

Page 77: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Barrier - kodstatic Barrier sync = new Barrier(3);

static CancellationToken token;

static void DriveToSeattle(string name, TimeSpan timeToGasStation)

{

try { // Drive to gas station

Console.WriteLine("[{0}] Leaving House", name);

Thread.Sleep(timeToGasStation);

Console.WriteLine("[{0}] Arrived at Gas Station", name);

sync .SignalAndWait();

Console.WriteLine("[{0}] Leaving for Seattle", name);

catch(OperationCancelledException) {

Console.WriteLine("[{0}] Caravan was canceled", name);

}

}

Page 78: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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

Page 79: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Task – kontynuacja po nowemuvar basicTask = new Task<int> (()=> {return 15;} );

var awaiter = basicTask.GetAwaiter();

awaiter.OnCompleted (() =>

{

int result = awaiter. GetResult();

Console.WriteLine (result); // Writes 123

});

basicTask.Start();

Page 80: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Task – completition sourcepublic 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

Page 81: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

completition sourceMoże być wykorzystane do czekania np. na I/O

Niekoniecznie 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); };

Page 82: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

completition source – DoAfterTask 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 (() => DoSmthgAfter1s ());

//lub

DoAfter(1000). ContinueWith() (ant => DoSmthgAfter1s ());

Page 83: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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

Page 84: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Operacje asynchroniczne Model zalecany dla np. dla metro, SL, wydajnego serwer

wielowatkowy I/O bounded

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

Page 85: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Operacje asynchroniczne APM Zrób swoje z użyciem CPU, a potem wyjdź i pozwól

wywołać Call-back

IAsyncResult BeginXXX (args, AsyncCallback callback, object state);

public delegate void AsyncCallback (IAsyncResult ar);

return-type EndXXX (args, IAsyncResult ar);

N.p.: dla stream:

public IAsyncResult BeginRead (byte[] buffer, int offset, int size, AsyncCallback callback, object state);

public int EndRead (IAsyncResult asyncResult);

Page 86: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

IAsyncResultpublic interface IAsyncResult {

// "state" object passed to Begin.

object AsyncState { get; }

// Signaled when complete.

WaitHandle AsyncWaitHandle { get; }

// Did it complete on BeginX (quick, APN-not supported, //CPU-bond so completed wthout blocking?

bool CompletedSynchronously { get; }

// Has it completed yet?

bool IsCompleted { get; }

}

Page 87: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

TPL vs AsyncTaskFactory.FromAsync( BeginXXX method EndXXX method Dodatkowe parametry

Task<int> readChunk = Task<int>.Factory.FromAsync (stream.BeginRead, stream.EndRead, buffer, 0, 1000, null);

Page 88: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Przykład wielowątkowyint 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));

}

Page 89: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

P. asynchroniczny “naiwny”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() + “...

"));

}

}

Koniec zanim liczby zostaną wypisane

Page 90: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Przykład asynchronicznyvoid 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

Page 91: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Przykład asynchroniczny IIclass 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*1000000+2, 1000000).GetAwaiter();

awaiter.OnCompleted (() => { Console.WriteLine (awaiter.GetResult()); if (i++ < MaxCount) DisplayPrimeCountsFrom (i); else { Console.WriteLine ("Done"); _tcs.SetResult

(null); }

});

}

}

Page 92: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Przykład asynchroniczny II cd.Task DisplayPrimeCountsAsync() {

var machine = new PrimesStateMachine();

machine.DisplayPrimeCountsFrom(0);

return machine.Task;

}

Page 93: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

C# 5.0 Kod:

var result = await wyrażenie;

Instukcja(je);

Jest rozwijany do:

var awaiter = wyrażenie.GetAwaiter();

awaiter.OnCompleted (() =>

{

var result = awaiter.GetResult();

Instukcja(je);

);

Page 94: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Przykład asynchroniczny 5.0Task<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*1000000+2,

1000000));

}

Page 95: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Przykład asynchroniczny 5.0async void DisplayPrimesCount()

{

int result = await GetPrimesCountAsync (2, 1000000);

Console.WriteLine (result);

}

Odpowiada funkcjonalnie następującemu kodowi

void DisplayPrimesCount() {

var awaiter = GetPrimesCountAsync (2, 1000000).GetAwaiter();

awaiter.OnCompleted (() => {

int result = awaiter.GetResult();

Console.WriteLine (result);

});

}

Page 96: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

C# 5.0 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

Page 97: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

C# 5.0 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).

Page 98: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

Równoległość ponownieWołanie metod async bez await powoduje ich równoległe

(thread pool) wykonanie.

async Task DoSmthg1()

{

}

async Task DoSmthg2()

{

}

var task1 = DoSmthg1();

var task2 = DoSmthg2();

Page 99: Programowanie Aplikacji Lokalnych w  Środowisku  .NET

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ć