Előadást letölteni
Az előadás letöltése folymat van. Kérjük, várjon
KiadtaKornél Illés Megváltozta több, mint 6 éve
1
Web programozás és haladó fejlesztési technikák Task async-await
Adatpárhuzamosság
2
Task
3
Emlékeztető A ParameterizedThreadStart delegált egy void(object) szignatúrájú metódus Szálnak bemeneti paramétert objectként kell átpasszolni a Start() metódus paraméterezésével static void Koszon(object o) { //string nev = (string)o; string nev = o.ToString(); //ebben az esetben eleg ez is Console.WriteLine("Hello " + o + "!"); } static void Main(string[] args) Thread t = new Thread(Koszon); Console.WriteLine("Szál indítása!"); t.Start("Pistike"); Console.WriteLine("Várakozás a befejezésre..."); t.Join(); Console.ReadLine();
4
Thread Alacsony szintű eszköz szálak definiálására
Nehezen kezelhetőek az egymásra épülések Költséges létrehozni, megszüntetni, váltani közöttük Nincs beépített lehetőség „barátságos” leállításra Nincs visszatérési érték definiálására lehetőség A metódus felparaméterezése sem túl fejlesztőbarát
5
Task Aszinkron elvégzett „feladat”
A háttérben egy ThreadPool egy eleme van: magasabb absztrakciós szinten dolgozunk Lehetőségek Visszatérési érték Nem-blokkoló egymásra épülések Kivételkezelés Leállítás
6
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 Könnyen alkalmazható, robosztus eszköz Széleskörű alkalmazási lehetőségek (feladatmegszakítás, kivételkezelés, ütemezés, stb) 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”).
7
Task indítása Taskok indítása paraméterrel:
Task task1 = new Task(new Action(PrintMessage)); Task task2 = new Task(delegate { PrintMessage(); }); Task task3 = new Task(() => PrintMessage()); Task task4 = new Task(() => { PrintMessage(); }); Taskok indítása paraméterrel: A new Task Action-t vagy Action<object>-et vár el, a Task.Run csak Action-t! 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); 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.
8
Task visszatérési értékkel
Taskok indítása visszatérési értékkel rendelkező művelet esetén: 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) Task<int> task = new Task<int>(LongOperationWithReturnValue); task.Start(); // Func<Tresult> ! // ... más műveletek... int result = task.Result; Console.WriteLine(result);
9
Thread esetén közös változóba gyűjtenénk a „kimenetet”
static void Main(string[] args) { int result = 0; Thread t1 = new Thread(() => { Thread.Sleep(1500); result = 42; }); t1.Start(); Console.WriteLine("Feldolgozás elindult!"); t1.Join(); Console.WriteLine("Feldolgozás vége!"); Console.WriteLine($"Eredmény: {result}"); Console.ReadLine(); } Feldolgozás elindult! Feldolgozás vége! Eredmény: 42
10
Task esetén adott a Result tulajdonságon keresztül
static void Main(string[] args) { Task<int> t1 = new Task<int>(() => { Thread.Sleep(1500); return 42; }); t1.Start(); Console.WriteLine("Feldolgozás elindult!"); t1.Wait(); Console.WriteLine("Feldolgozás vége!"); Console.WriteLine($"Eredmény: {t1.Result}"); Console.ReadLine(); } Feldolgozás elindult! Feldolgozás vége! Eredmény: 42
11
Task esetén a Result lekérdezése blokkol
static void Main(string[] args) { Task<int> t1 = new Task<int>(() => { Thread.Sleep(1500); return 42; }); t1.Start(); Console.WriteLine("Feldolgozás elindult!"); Console.WriteLine($"Feldolgozás vége!\nEredmény: {t1.Result}"); Console.ReadLine(); } Feldolgozás elindult! Feldolgozás vége! Eredmény: 42
12
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 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
13
Több Task együttes bevárása
List<Task> ts = new List<Task>() { new Task(() => { Thread.Sleep(800); Console.WriteLine("Kész vagyok!"); }), new Task(() => { Thread.Sleep(1200); Console.WriteLine("Kész!"); }), new Task(() => { Thread.Sleep(1000); Console.WriteLine("Elkészült!"); }), new Task(() => { Thread.Sleep(1100); Console.WriteLine("Ready!"); }), }; foreach (Task t in ts) t.Start(); Task.WaitAll(ts.ToArray()); Console.WriteLine("Minden feladat végrehajtva!"); Kész vagyok! Elkészült! Ready! Kész! Minden feladat végrehajtva!
14
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);
15
Task folytatása Task t = new Task(() => { Thread.Sleep(1000); Console.WriteLine("Kész!"); }); t.ContinueWith(x => { Console.WriteLine($"Task #{x.Id} véget ért!"); }); t.Start(); Console.WriteLine("Task elindítva!"); Task elindítva! Kész! Task #2 véget ért!
16
Több Task együttes folytatása
List<Task> ts = new List<Task>() { new Task(() => { Thread.Sleep(800); Console.WriteLine("Kész vagyok!"); }), new Task(() => { Thread.Sleep(1200); Console.WriteLine("Kész!"); }), new Task(() => { Thread.Sleep(1000); Console.WriteLine("Elkészült!"); }), new Task(() => { Thread.Sleep(1100); Console.WriteLine("Ready!"); }), }; Task.WhenAll(ts.ToArray()).ContinueWith(x => { Console.WriteLine("Minden feladat végrehajtva!"); }); foreach (Task t in ts) t.Start(); Console.ReadLine(); Kész vagyok! Elkészült! Ready! Kész! Minden feladat végrehajtva!
17
Eredménnyel rendelkező Taskok együttes folytatása
List<Task<int>> ts = new List<Task<int>>() { new Task<int>(() => { Thread.Sleep(800); return 800; }), new Task<int>(() => { Thread.Sleep(600); return 600; }), new Task<int>(() => { Thread.Sleep(1200); return 1200; }), new Task<int>(() => { Thread.Sleep(1100); return 1100; }), }; Task.WhenAll(ts.ToArray()).ContinueWith(x => { //x.Result: int[] Console.WriteLine("Vége! Az eredmények összege: " + x.Result.Sum()); }); foreach (var t in ts) t.Start(); Console.ReadLine(); Vége! Az eredmények összege: 3700
18
Task elindult Baj történt, az alábbi üzenettel: One or more errors occurred. Hibakezelés a Taskban 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 Task t = new Task(() => { Thread.Sleep(500); throw new Exception("houston, baj van!"); Console.WriteLine("Ez sosem íródik ki."); }); t.Start(); Console.WriteLine("Task elindult"); try { t.Wait(); } catch (Exception e) { Console.WriteLine("Baj történt, az alábbi üzenettel: " + e.Message); }
19
Hibakezelés a Taskban 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 Task t = new Task(() => { Thread.Sleep(500); throw new Exception("houston, baj van!"); Console.WriteLine("Ez sosem íródik ki."); }); t.Start(); Console.WriteLine("Task elindult"); try { t.Wait(); } catch (AggregateException e) { foreach (var ie in e.InnerExceptions) Console.WriteLine("Baj történt, az alábbi üzenettel: " + ie.Message); }
20
Task elindult Baj történt, az alábbi üzenettel: houston, baj van! Hibakezelés a Taskban 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 Task t = new Task(() => { Thread.Sleep(500); throw new Exception("houston, baj van!"); Console.WriteLine("Ez sosem íródik ki."); }); t.Start(); Console.WriteLine("Task elindult"); try { t.Wait(); } catch (AggregateException e) { Console.WriteLine("Baj történt, az alábbi üzenettel: " + string.Join("\n", e.InnerExceptions.Select(x => x.Message))); }
21
AggregateException Task t = new Task(() => {
new Task(() => { throw new Exception("asd"); }, TaskCreationOptions.AttachedToParent).Start(); Thread.Sleep(500); throw new Exception("houston, baj van!"); Console.WriteLine("Ez sosem íródik ki."); }); t.Start(); Console.WriteLine("Task elindult");
22
AggregateException try { t.Wait(); } catch (AggregateException e) {
e.Handle(ie => { if (ie.Message.Contains("houston")) Console.WriteLine("ezt ismerjük, kezeljük"); return true; } Console.WriteLine("Baj történt, az alábbi üzenettel: " + ie.Message); return false; }); Task elindult ezt ismerjük, kezeljük Baj történt, az alábbi üzenettel: One or more errors occurred. Unhandled Exception: System.AggregateException: One or more errors occurred. ---> System.AggregateException: One or more errors occurred. ---> System.Exception: asd at xx_Eloadashoz.Program.<>c.<Main>b__0_1() in ... Press any key to continue . . .
23
Hibakezelés Continuationnel
Task t = new Task(() => { new Task(() => { throw new Exception("asd"); }, TaskCreationOptions.AttachedToParent).Start(); Thread.Sleep(500); throw new Exception("houston, baj van!"); Console.WriteLine("Ez sosem íródik ki."); }); t.ContinueWith(x => { Console.WriteLine("Hiba volt: " + x.Exception.Message); }, TaskContinuationOptions.OnlyOnFaulted); t.Start(); Console.WriteLine("Task elindult"); Task elindult Hiba volt: One or more errors occurred.
24
Task leállítása A Task-ban végzett műveletben kell kezelni a leállítás lehetőségét Szinkronizált CancellationTokenen keresztül történik a Task értesítése, így megoldva a közös változó használatának problémáját 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 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.
25
Task leállítása CancellationTokenSource cts = new CancellationTokenSource(); Task t = new Task(() => { for (int i = 0; i < 1000; i++) { Thread.Sleep(10); Console.Write(i + "\t"); if (cts.Token.IsCancellationRequested) return; } }, cts.Token); t.Start(); Console.WriteLine("Task elindult!"); Thread.Sleep(1200); Console.WriteLine("Leállítás kezdeményezése..."); cts.Cancel(); Task elindult! Leállítás kezdeményezése... 54
26
Task leállítása - kivétel
CancellationTokenSource cts = new CancellationTokenSource(); Task t = new Task(() => { for (int i = 0; i < 1000; i++) { Thread.Sleep(10); Console.Write(i + "\t"); cts.Token.ThrowIfCancellationRequested(); } }, cts.Token); t.Start(); Console.WriteLine("Task elindult!"); Thread.Sleep(1200); Console.WriteLine("Leállítás kezdeményezése..."); cts.Cancel();
27
Task leállítása try { t.Wait(); } catch (AggregateException e) {
e.Handle(ie => { if (ie is OperationCanceledException) Console.WriteLine("Leállítva!"); return ie is OperationCanceledException; }); } Task elindult! Leállítás kezdeményezése... Leállítva!
28
Task leállítása - continuation
CancellationTokenSource cts = new CancellationTokenSource(); Task t = new Task(() => { for (int i = 0; i < 1000; i++) { Thread.Sleep(10); Console.Write(i + "\t"); cts.Token.ThrowIfCancellationRequested(); } }, cts.Token); t.ContinueWith(x => { Console.WriteLine("Leállítva!"); }, TaskContinuationOptions.OnlyOnCanceled); t.Start(); Console.WriteLine("Task elindult!"); Thread.Sleep(600); Console.WriteLine("Leállítás kezdeményezése..."); cts.Cancel();
29
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(...); });
30
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.
31
async-await
32
Aszinkronitás „Asynchrony is essential for activities that are potentially blocking (…). Access to a web resource sometimes is slow or delayed. If such an activity is blocked within a synchronous process, the entire application must wait. In an asynchronous process, the application can continue with other work that doesn't depend on the web resource until the potentially blocking task finishes.” JavaScript Az aszinkron kódolásra kiváló eszköz a JavaScript nyelv használata: az alapból aszinkron felépítésű kódban minden hosszadalmas függvényhívás, ahol erőforrásra kell várakozni, átlépésre kerül. Amikor a folyamat végzett, a visszatérésével egy úgynevezett callback-function foglalkozik.
33
Szinkron példa Console.WriteLine("Megkezdem a letöltést...");
string tartalom = new WebClient().DownloadString(" Console.WriteLine("Letöltve!"); Console.WriteLine(tartalom.Contains("Lannister") ? "Van benne Lannister" : "Nincs benne Lannister"); Megkezdem a letöltést... Letöltve! Van benne Lannister
34
Klasszikus aszinkron megoldás esemény alapon
static void Main(string[] args) { Console.WriteLine("Megkezdem a letöltést..."); WebClient wc = new WebClient(); wc.DownloadStringCompleted += Wc_DownloadStringCompleted; wc.DownloadStringAsync(new Uri(" Console.ReadLine(); } private static void Wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) Console.WriteLine("Letöltve!"); Console.WriteLine(e.Result.Contains("Lannister") ? "Van benne Lannister" : "Nincs benne Lannister");
35
Task alapú megoldás Console.WriteLine("Megkezdem a letöltést...");
Task<string> t = new WebClient().DownloadStringTaskAsync(" t.ContinueWith(x => { Console.WriteLine("Letöltve!"); Console.WriteLine(x.Result.Contains("Lannister") ? "Van benne Lannister" : "Nincs benne Lannister"); });
36
async-await async Task<string> AszinkronValasz() {
await Task.Delay(2000); return "kész"; } await „bevárás” A végrehajtás szüneteltetése, amíg a „bevárt” Task nem végez awaitelni Task-ot lehet, akár visszatérési értékkel Sok beépített nyelvi elem (GetXXXAsync()) async Ahol awaitelsz valamit, azt a metódust az async kulcsszóval meg kell jelölni Csak void, Task és Task <TResult> visszatérésű metódusok lehetnek async-ek
37
async-await alapú megoldás
static async void Elemez(string url) { Console.WriteLine("Letöltés megkezdve!"); string tartalom = await new WebClient().DownloadStringTaskAsync(url); Console.WriteLine("Letöltve!"); Console.WriteLine(url + ": " + (tartalom.Contains("Lannister") ? "van benne Lannister" : "nincs benne Lannister")); }
38
async-await alapú megoldás
static void Main(string[] args) { Console.WriteLine("Megkezdem a letöltéseket..."); string urls = " foreach (var url in urls.Split(',').Select(x => x.Trim())) Elemez(url); Console.WriteLine(url + " elemzése elindult"); } Console.ReadLine(); static async void Elemez(string url) {…} Megkezdem a letöltéseket... Letöltés megkezdve! elemzése elindult elemzése elindult elemzése elindult elemzése elindult Letöltve! van benne Lannister nincs benne Lannister nincs benne Lannister nincs benne Lannister
39
async-await a Main()-ben
static async Task Main(string[] args) { Console.WriteLine("Megkezdem a letöltést..."); string tartalom = await new WebClient().DownloadStringTaskAsync(" Console.WriteLine("Letöltve!"); Console.WriteLine(tartalom.Contains("Lannister") ? "Van benne Lannister" : "Nincs benne Lannister"); Console.ReadLine(); } C#7 óta lehet ilyet
40
Vizuális alkalmazásoknál látványos
async Task<string> AszinkronValasz() { await Task.Delay(2000); return "kész"; } private async void button_Click(object sender, RoutedEventArgs e) textBox.Text = await AszinkronValasz();
41
Párhuzamos? Valójában önmagában nincs külön szál
Az async módosítószóval megjelölt metódus az await kulcsszóval megjelölt hívásig szinkron kerül végrehajtásra Ott azonban a metódus „suspended” állapotú lesz, amíg a Task nem végez A Task persze „dönthet úgy” hogy külön szálat alkalmaz Amíg a metódus áll, a vezérlés visszatér a hívás helyére, és továbblép Amikor a Task végzett, a vezérlés visszakerül oda
42
Adatpárhuzamos megoldások
43
Parallel class Parallel.For(int, int, Action<>)
Parallel.ForEach(IEnumerable<>, Action<>) Parallel.Invoke(Action[])
44
Parallel.For Gyakorlatilag a for ciklus magjának kifejtése és Action<int> delegált meghívása minden ciklusváltozóra int[] eredm = new int[10]; Parallel.For(0, eredm.Length, i => { eredm[i] = i * i; Console.Write(i + " kész! "); } ); Console.WriteLine(); for (int i = 0; i < eredm.Length; i++) Console.Write(i + ": " + eredm[i] + ", "); vigyázni, ha összefüggés van az adatok között: versenyhelyzet! 0 kész! 1 kész! 3 kész! 4 kész! 5 kész! 6 kész! 7 kész! 8 kész! 9 kész! 2 kész! 0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81,
45
Parallel.ForEach Hasonlóan a Parallel.For()-hoz, a gyűjtemény típusának megfelelő Action<T> delegáltat alkalmaz Parallel.ForEach("hello world!", e => { Console.Write(char.ToUpper(e)); }); Console.ReadLine(); OHELWRD!LO L
46
Parallel.Invoke A Parallel.Invoke() egy Action[] bemenetet kap paraméterként, amely delegáltakat párhuzamosan igyekszik végrehajtani Az utasítás blokkol, addig nem lép tovább amíg minden Action végrehajtása véget nem ért Parallel.Invoke( BasicAction, // Param #0 - static method () => // Param #1 - lambda expression { Console.WriteLine("Method=beta,Thread={0}", Thread.CurrentThread.ManagedThreadId); }, delegate () // Param #2 - in-line delegate Console.WriteLine("Method=gamma,Thread={0}", Thread.CurrentThread.ManagedThreadId); } );
47
Források Szabó-Resch Miklós Zsolt és Cseri Orsolya Eszter Haladó Programozás előadásfóliái Kertész Gábor Párhuzamos és Elosztott Rendszerek Programozása előadásfóliái MSDN
Hasonló előadás
© 2024 SlidePlayer.hu Inc.
All rights reserved.