Az előadás letöltése folymat van. Kérjük, várjon

Az előadás letöltése folymat van. Kérjük, várjon

Haladó Programozás System.Threading.Tasks.Task OE-NIK HP.

Hasonló előadás


Az előadások a következő témára: "Haladó Programozás System.Threading.Tasks.Task OE-NIK HP."— Előadás másolata:

1 Haladó Programozás System.Threading.Tasks.Task OE-NIK HP

2 Egy szál / több szál? A többszálúság biztosítja a program/OS válaszkészségét, de... A szál DRÁGA (megj: OS-függő, Windowsra vonatkozik) Memóriafoglalás szálanként: Stack, szálkontextus (stb.) Szálak használatából adódó processzoridő-veszteség: Időosztás menedzselésével elvesztett idő Ideális: annyi szál/program, ahány mag Szálváltáskor a futási környezet változásából adódó cache miss-ek Újabb szálváltáskor újabb cache miss-ek... Szál létrehozásakor és megszüntetésekor a programban betöltött natív .dll-ekben Dll_Main(DLL_THREAD_ATTACH), Dll_Main(DLL_THREAD_DETACH) hívások történnek Megj: powerpnt.exe 147 dll!!!! ... VS 2012: 183 dll... Szál overhead < process overhead Konklúzió: alapos előny/hátrány mérlegelés után használjuk csak OE-NIK HP

3 Task A szálak drágák, létrehozásuk különösen erőforrásigényes
Régebbi megoldás: ThreadPool Előre létrehozott szálakat tartalmaz, ezekre oszthatók ki feladatok A használattól függően menedzseli a szálak számát Hátrányok: A szálak kívülről nem elérhetők (kész van-e?) Visszatérési érték adását nem támogatja A Taskokat ún. TaskScheduler osztja ki (többnyire) a ThreadPool száljaira, intelligens módon Hosszú műveleteknél ThreadPoolon kívüli szál létrehozására is képes static void Main(string[] args) { ThreadPool.QueueUserWorkItem(ComputeBoundOp, 5); } private static void ComputeBoundOp(object state) { /* Long running operation */ } Mindkét hátrány megkerülhető, de kellemetlenek. When a thread calls the Wait method, the system checks if the Task that the thread is waiting for has started executing. If it has, then the thread calling Wait will block until the Task has completed running. But if the Task has not started executing yet, then the system may (depending on the TaskScheduler) execute the Task by using the thread that called Wait. If this happens, then the thread calling Wait does not block; it executes the Task and returns immediately. To help you detect unobserved exceptions, you can register a callback method with TaskScheduler’s static UnobservedTaskException event. The task infrastructure is very flexible, and TaskScheduler objects are a big part of this flexibility. A TaskScheduler object is responsible for executing scheduled tasks and also exposes task information to the Visual Studio debugger. The FCL ships with two TaskScheduler-derived types: the thread pool task scheduler and a synchronization context task scheduler. By default, all applications use the thread pool task scheduler. This task scheduler schedules tasks to the thread pool’s worker threads and is discussed in more detail in this chapter’s “How the Thread Pool Manages Its Threads” section. You can get a reference to the default task scheduler by querying TaskScheduler’s static Default property. The synchronization context task scheduler is typically used for applications sporting a graphical user interface, such as Windows Forms, Windows Presentation Foundation (WPF), Silverlight, and Windows Store applications. This task scheduler schedules all tasks onto the application’s GUI thread so that all the task code can successfully update UI components like buttons, menu items, and so on. The synchronization context task scheduler does not use the thread pool at all. You can get a reference to a synchronization context task scheduler by querying TaskScheduler’s static FromCurrentSynchronizationContext method. OE-NIK HP

4 Hagyományos szál vs. Task
Használjunk hagyományos szálakat, ha: A szálnak a normálistól eltérő prioritással kell futnia – a ThreadPool-on minden szál normál prioritással fut Előtérszálra van szükség – minden ThreadPool szál háttérszál A szál extrém hosszú műveletet végez (pl. az alkalmazás teljes élettartama alatt futnia kell) Abort()-ra lehet szükség Minden más esetben Task ajánlott A szál extrém hosszú műveletet végez (pl. az alkalmazás teljes élettartama alatt futnia kell) Ebben amúgy nem vagyok biztos, hogy még most is így van, lásd LongRunning (ilyenkor nem threadpoolra teszi). De mindenesere ilyenkor nem árt meg a nem Task szál. Abort: _azonnali_ kinyírása a szálnak, ez a tasknál nincs, csak cancel. Mellesleg threadeknél meg egyebeknél is nagyon ellenjavallt az abort. You need the thread to run with a non-normal thread priority. All thread pool threads run at normal priority. Although you can change this, it is not recommended, and the priority change does not persist across thread pool operations. ■■ You need the thread to behave as a foreground thread, thereby preventing the application from dying until the thread has completed its task. For more information, see the ”Foreground Threads vs. Background Threads” section later in this chapter. Thread pool threads are always background threads, and they may not complete their task if the CLR wants to terminate the process. ■■ The compute-bound task is extremely long-running; this way, I would not be taxing the thread pool’s logic as it tries to figure out whether to create an additional thread. ■■ You want to start a thread and possibly abort it prematurely by calling Thread’s Abort method (discussed in Chapter 22, “CLR Hosting and AppDomains”). OE-NIK HP

5 Task indítása Nem garantált a külön szál – TaskScheduler dönti el
Taskok indítása paraméter nélkül, paraméterrel: A new Task Action-t vagy Action<object>-et vár el, a Task.Run csak Action-t! Taskok indítása visszatérési értékkel rendelkező művelet esetén: Az összes fenti módszerrel lehet visszatérési értékkel rendelkező Taskot indítani A Result tulajdonság megvárja a művelet végét (blokkol), ezért érdemes nem azonnal a Start után hívni (vagy lásd később) new Task(LongOperation).Start(); new Task(LongOperationWithParam, 15).Start(); Task t = Task.Run(() => LongOperation()); //.NET 4.5 Task t = Task.Run(() => LongOperationWithParam(15)); //.NET 4.5 Task t = new TaskFactory().StartNew(LongOperation); Task<int> task = new Task<int>(LongOperationWithReturnValue); task.Start(); // Func<Tresult> ! // ... más műveletek... int result = task.Result; Console.WriteLine(result); Factory-s megoldásnak sok-sok overloadja van. Valamint egy factory-t felparaméterezhetünk, ahogy akarunk, és az adott paraméterekkel indíthatunk akár több darab Taskot is. (A példa nem mutatja meg ennek a kihasználását) Task.Run, new segítségével is lehet visszatérési értékkel rendelkező Taskot indítani.

6 Várakozás Taskra Bármilyen esetben, amikor a Task műveletére várni kell (pl. eredményt képez, tömböt feltölt, beállítást végez, fájlt ment…) Hátrány: ezek a hívások mind blokkolnak Megoldás: Continuation-ök task.Wait(); //várakozás, míg kész (blokkol) Task.WaitAll(taskArray); //várakozás, míg mind kész (blokkol) Task.WaitAny(taskArray); //várakozás, míg legalább az egyik kész (blokkol) //vagy Task.WaitAll(task1, task2, task3); //mint fent Task.WaitAny(task1, task2, task3); //mint fent OE-NIK HP

7 Continuation-ök Az eredeti Task lefutása után egy új Task indul majd a megadott művelettel (a t az előző, befejeződött Taskra való referencia) Feltételes indítás: Task<int> continuedTask = new Task<int>(LongOperationWithReturnValue); //nem blokkol, mint a Wait: continuedTask.ContinueWith(t => Console.WriteLine("The result was: " + t.Result)); //csak ha hiba nélkül futott az eredeti Task: continuedTask.ContinueWith(t => Console.WriteLine("The result was: " + t.Result), TaskContinuationOptions.OnlyOnRanToCompletion); //csak ha cancelezték az eredeti Taskot: continuedTask.ContinueWith(t => Console.WriteLine("The task was canceled!"), TaskContinuationOptions.OnlyOnCanceled); //csak ha hibára futott az eredeti Task: continuedTask.ContinueWith(t => Console.WriteLine("The task threw " + "an exception: " + t.Exception.InnerException), TaskContinuationOptions.OnlyOnFaulted); OE-NIK HP

8 Task hibakezelés Ha egy Taskban hiba történik, az Exception lenyelődik, és a Wait() vagy Result hívásakor dobódik el egy gyűjteményes Exception (AggregateException) formájában Egy Tasknak lehet több gyermek taskja is: több Exception is elképzelhető Az AggregateException példányban InnerExceptions gyűjteményben vannak a konkrét kivételpéldányok Task<int> taskWithException = new Task<int>(LongOperationWithReturnValueAndException); taskWithException.Start(); try { result = taskWithException.Result; } catch (AggregateException ex) foreach (Exception innerException in ex.InnerExceptions) Console.WriteLine(ex.Message); Érdekes még, és ha akarod, mutasd meg: ex.Handle(). Bár van rá példa a cancelnél később! https://msdn.microsoft.com/en-us/library/dd537614%28v=vs.110%29.aspx OE-NIK HP

9 Task hibakezelés Másik megoldás feltételes continuation segítségével:
Task taskWithContinuation = new Task(LongOperationWithException); taskWithContinuation.ContinueWith((t) => { Console.WriteLine("THERE WAS AN EXCEPTION: {0}", t.Exception.Message); }, TaskContinuationOptions.OnlyOnFaulted); taskWithContinuation.Start(); OE-NIK HP

10 Task leállítása (canceling)
A Task-ban végzett műveletben kell kezelni a leállítás lehetőségét Ún. CancellationTokenen keresztül történik a Task értesítése Használható ellenőrzési módok: cancellationToken.IsCancellationRequested: bool tulajdonság. Ha igaz, ki kell lépni a függvényből cancellationToken.ThrowIfCancellationRequested(): Exception keletkezik, ha leállítás volt kérelmezve (ezzel kilép a függvényből) Előny: az Exception egyértelműen mutatja, hogy a leállás nem a művelet vége miatt történt static void CancellableLongOperation(CancellationToken cancellationToken) { for (int i = 0; i < 100; i++) cancellationToken.ThrowIfCancellationRequested(); Thread.Sleep(100); } The Microsoft .NET Framework offers a standard pattern for canceling operations. This pattern is cooperative, meaning that the operation that you want to cancel has to explicitly support being canceled. OE-NIK HP

11 Task leállítása (canceling)
CancellationTokenSource source = new CancellationTokenSource(); Task taskWithCancel = Task.Run(() => CancellableLongOperation(source.Token), source.Token); source.Cancel(); try { taskWithCancel.Wait(); } catch (AggregateException ex) ex.Handle(e => e is OperationCanceledException); Console.WriteLine("A műveletet leállították."); ex.Handle lekezeltnek nyilvánítja az AggregateExceptionben található kivételeket, ha azok megfelelnek a megadott feltételnek Amennyiben maradt még kezeletlen kivétel, új kivételdobás történik Ha nem maradt kezeletlen kivétel, nincs dobás, lefut az utolsó sor is CancellationToken.None adható át, ha le akarjuk tiltani a leállítást OE-NIK HP

12 GUI-elem kezelése Windows-os grafikusfelület-elemekhez általában nem lehet hozzányúlni, csak a létrehozó szálról (GUI szál) Még közvetve sem WPF, Windows Forms is! Van kevés kivétel (bizonyos függvények, adatkötésnél a PropertyChanged) Általános megoldás: Invoke Függvény végrehajtatása a GUI szállal Dispatcher.Invoke(() => { label.Content = ...; listBox.Items.Add(...); }); OE-NIK HP

13 GUI-elem kezelése Rövidebb módszer, ha a Task eredményét kiíró műveletet is külön Taskként indítjuk, megadott taskütemező (TaskScheduler) segítségével Beépített taskütemezők: Thread Pool Task Scheduler: a ThreadPool-on indítja a taskokat (alapértelmezett) Synchronization Context Task Scheduler: a felhasználói felület szálján indítja a taskokat – ezzel kell indítani, ha GUI-elemet akarunk kezelni Referencia „megszerzése”: a GUI szálján TaskScheduler.FromCurrentSynchronizationContext() Task<int> taskWithReturnValue = Task.Run(() => { Thread.Sleep(1000); return 6; }); taskWithReturnValue.ContinueWith( t => textBox1.Text = "Result: " + t.Result, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext()); The task infrastructure is very flexible, and TaskScheduler objects are a big part of this flexibility. A TaskScheduler object is responsible for executing scheduled tasks and also exposes task information to the Visual Studio debugger. The FCL ships with two TaskScheduler-derived types: the thread pool task scheduler and a synchronization context task scheduler. By default, all applications use the thread pool task scheduler. This task scheduler schedules tasks to the thread pool’s worker threads and is discussed in more detail in this chapter’s “How the Thread Pool Manages Its Threads” section. You can get a reference to the default task scheduler by querying TaskScheduler’s static Default property. The synchronization context task scheduler is typically used for applications sporting a graphical user interface, such as Windows Forms, Windows Presentation Foundation (WPF), Silverlight, and Windows Store applications. This task scheduler schedules all tasks onto the application’s GUI thread so that all the task code can successfully update UI components like buttons, menu items, and so on. The synchronization context task scheduler does not use the thread pool at all. You can get a reference to a synchronization context task scheduler by querying TaskScheduler’s static FromCurrentSynchronizationContext method. You can, of course, define your own class derived from TaskScheduler if you have special task scheduling needs. Microsoft has provided a bunch of sample code for tasks and includes the source code for a bunch of task schedulers in the Parallel Extensions Extras package, which can be downloaded from here: Here are some of the task schedulers included in this package: ■■ IOTaskScheduler This task scheduler queues tasks to the thread pool’s I/O threads instead of its worker threads. ■■ LimitedConcurrencyLevelTaskScheduler This task scheduler allows no more than n (a constructor parameter) tasks to execute simultaneously. OrderedTaskScheduler This task scheduler allows only one task to execute at a time. This class is derived from LimitedConcurrencyLevelTaskScheduler and just passes 1 for n. ■■ PrioritizingTaskScheduler This task scheduler queues tasks to the CLR’s thread pool. After this has occurred, you can call Prioritize to indicate that a Task should be processed before all normal tasks (if it hasn’t been processed already). You can call Deprioritize to make a Task be processed after all normal tasks. ■■ ThreadPerTaskScheduler This task scheduler creates and starts a separate thread for each task; it does not use the thread pool at all. OE-NIK HP

14 Feladat RSS olvasó – több forrásból töltse a frissítéseket, ÉS a listboxban időrendi sorrendben, forrástól függetlenül listázza. OE-NIK HP

15 Forrás: Jeffrey Richter: CLR via C# 4th edition
OE-NIK HP


Letölteni ppt "Haladó Programozás System.Threading.Tasks.Task OE-NIK HP."

Hasonló előadás


Google Hirdetések