1 Modernit sovellukset ja asynkronisuus Heikki Raatikainen Miksi asynkronisuutta? Käyttöliittymät (sekä web että client) Käyttöliittymän pitäminen 'hengissä' Näytön päivitys taustalla Suorituskyvyn parantaminen Moniytimisten prosessorien tehokkaampi käyttö Yksi säie yksi ydin käytössä Logiikka pilkottu pienempiin osiin eri säikeille voidaan ajaa rinnan eri ytimillä 2
2 Kosketusnäytöt uudet haasteet koodaajalle Kosketusnäytössä voidaan painonappia painaa, valita listasta, asettaa focus haluttuun kenttään jne aivan kuten missä tahansa muussakin näyttötyypissä Mutta: kosketusnäytössä olevan sovelluksen on reagoitava välittömästi ilman mitään viiveitä jokaiseen käyttäjän toimintoon Yhtään pitkäkestoista toimintoa ei voi tehdä synkronisesti eli sillä perinteisellä tavalla tai sovellus on käytännössä käyttökelvoton käyttöliittymäkoodi muuttuu ja ohjelmoijalla ei ole muuta vaihtoehtoa kuin opetella asynkroninen koodaus ja kaikki sen vaatimat asiat 3 Web sovelluksen nopeudella on väliä Lähde: http://alexmaccaw.com/posts/async_ui Amazon: 100 ms of extra load time caused a 1% drop in sales (source: Greg Linden, Amazon). Google: 500 ms of extra load time caused 20% fewer searches (source: Marrissa Mayer, Google). Yahoo!: 400 ms of extra load time caused a 5 9% increase in the number of people who clicked back before the page even loaded (source: Nicole Sullivan, Yahoo!). Siis sekä client- että server-koodi on optimoitava suorituskyvyn suhteen Client: JavaScript ja Ajax 4
3 JavaScript ja rinnakkaisuus Selaimien JavaScript on ollut yksisäikeistä rinnakkaisuus on pitänyt tehdä Timerkäsitteen avulla Toimii kaikilla selaimilla settimeout(haeuutinen, 1000); HTML5: säikeistys voidaan tehdä Web Workereillä var hakutoiminto = new Worker('haku.js'); function hae(query) { hakutoiminto.postmessage(query); 5 Perinteinen UI 'ongelma' Käynnistä toiminto, mahdollisesti keskeytä (haku tai prosessointi) täysin ratkaistavissa, vaatii rinnakkaisuutta ja sen ymmärtämistä 6
4 Mikä rinnakkaisuudessa on sitten vaikeaa? Ei pitäisi olla mitään vaikeaa, mutta käytäntö on osoittanut toista Teknisesti esimerkiksi.net kirjastossa ja kielissä on varmasti kaikki tarvittavat osat rinnakkaisuuden toteuttamiseen ja hallintaan Merkittävää on ymmärtää miksi rinnakkaisuutta (asynkronisia) toimintoja tarvitaan ja mitä muita vaatimuksia tulee esille (mm.): säie-turvallinen datan käsittely, synkronointimekanismit käyttöliittymän päivitys vain ja ainoastaan käyttöliittymän tehneeltä säikeeltä Saman datan käsittely eri säikeistä pitää suojata, muuten sovellus voi käyttäytyä epämääräisesti Toimintojen keskinäinen ajastaminen, tarvitaanko? Debuggaaminen on haasteellista 7 Historiasta nykyisyyteen säie (.NET ja Win32API) FX1.1 BackgroundWorker-komponentti Lambda-lausekkeet, Extension-metodit delegaatin Begin-/EndInvoke ThreadPool System.Threading.Timer class FX3.5 Synkronointiluokat: Mutex, Monitor, Semaphore WaitEventHandle, prosessoreiden hyödyntäminen, uudet ohjelmointimallit (Task) Parallel.Invoke, For ja ForEach FX4 CancellationToken Task, Task.Factory Barrier, CountdownEvent, Parallel LINQ SemaphoreSlim, SpinLock, SpinWait F# ConcurrentCollections Asynkroninen UI async ja await (C#5) FX4.5 FX2 8
5 Rinnakkaisuuden tekniikoita Thread Käynnistäminen delegaatin kautta: BeginInvoke/EndInvoke BackgroundWorker Parallel.For, Parallel.ForEach ja Parallel.Invoke Task PLINQ async ja await (C#5 ja Task-luokka) Patternit: Asynchronous Programming Model (APM) Event-based Asynchronous Pattern (EAP) Task-based Asynchronous Pattern (TAP) ja tietysti kaikki synkronointi- ja sarjallistamismekanismit 9 Patternien erot Synkroninen APM EAP TAP public class MyClass { public int Read(byte [] buffer, int offset, int count); public class MyClass { public IAsyncResult BeginRead(byte [] buffer, int offset, int count, AsyncCallback callback, object state); public int EndRead(IAsyncResult asyncresult); public class MyClass { public void ReadAsync(byte [] buffer, int offset, int count); public event ReadCompletedEventHandler ReadCompleted; public class MyClass { public Task<int> ReadAsync(byte [] buffer, int offset, int count); 10
6 Client-käyttöliittymät WinForms käyttöliittymän päivitys vain sillä säikeellä joka on luonut kontrollin, taustasäikeeltä siirretään suorituskonteksti oikealle säikeelle Invoke-metodilla WPF-sovelluksessa sama rajoite, mutta päivityksessä käytetään Dispacher-luokkaa, monipuolisempi kuin WinFormsin tekniikka private void taustahommadispatcher() { Action<int> lisäälistaan = j => listbox1.items.add(j); for (int i = 0; i < 10; i++) { Dispatcher.Invoke(lisääListaan, DispatcherPriority.Normal, i); 11 BackgroundWorker komponentti Yksinkertaistaa taustasäikeiden käyttöä tuli versioon FX2 helpottamaan 'vaikeaa' rinnakkaisuuden toteuttamista RunWorkerAsync metodi käynnistää toiminnan Toimintologiikka koodataan DoWork tapahtumaan ProgressChanged ja RunWorkerCompleted tapahtumat suorittuvat "pääsäikeessä" Voi käyttää kaikissa sovellustyypeissä, esim. Service:ssä ja konsolisovelluksessa yhtä hyvin kuin Windows-sovelluksessa (WinForms ja WPF) tai Web-sovelluksessakin 12
7 Teknisiä rajoitteita ja ongelmia Säikeet eivät ole ilmaisia, niitä käytettäessä tulee Context switch:ejä jotka kuluttavat resursseja ja moniytimisten prosessorien tehokas käyttäminen on haastavaa ThreadPool on ollut tehokkaampi tapa, mutta käynnistetyn metodin tilaa on mahdotonta hallita/valvoa, esim ei tietoa milloin metodi on päättynyt: var mre = new ManualResetEvent(false); ThreadPool.QueueUserWorkItem( (o) => { PitkäHomma(); mre.set();); mre.waitone(); 13 Task Parallel Library PLINQ LINQ-kyselyn suorittaminen pienissä osissa rinnakkain (.AsParallel()) Parallel Helppo tapa käynnistää metodeja rinnakkain suoritukseen ja odottaa niiden päättymistä Parallel.Invoke, Parallel.For ja Parallel.ForEach, näiden tehokas hyödyntäminen edellyttää että rinnakkain suoritettavilla toiminnoilla ei ole keskinäisiä riippuvuuksia Jos on, niin rinnakkaisuuden määrä pienenee ja hyödyt jäävät vähäisemmiksi Task Rinnakkaisuuden merkittävin käsite.net4:stä lähtien Ajatusmallimuutos: Task on enemmän looginen käsite kuin tekniikka.net tarjoaa paljon asynkronisia palveluita jotka perustuvat Task-käsitteeseen Toimintojen keskeytys: CancellationToken ja CancellationTokenSource 14
8 Parallel LINQ LINQ-kyselyiden suorittaminen rinnakkain, voidaan määritellä monellako prosessorilla/ytimellä suoritetaan samaan aikaan automaattisesti kaikki kyselyt eivät toimi rinnakkain saattaa olla riippuvuuksia tai rajoituksia oletus sama kuin aikaisemmin yhteensopivuus 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; 15 Parallel-luokka Parallel.Invoke rinnakkaisten toimintojen käynnistys, toiminnoilla ei sivuvaikutuksia toistensa toimintoihin Parallel.For saman metodin käynnistäminen useita kertoja rinnakkain private void button4_click(object sender, EventArgs e) { Action toiminto = () => Trace.WriteLine("toimii"); Parallel.Invoke(toiminto, ()=>homma2(42), laskentaa); private void homma2(int indeksi) { // koodia private void laskentaa() { // koodia 16
9 System.Threading.Tasks.Task class Task-luokka ja Task.Factory.StartNew mahdollistavat ns. tehtävien (ovat siis metodeja) käynnistämisen Parallel.Invoke:n tavoin, mutta Task-käsittelyssä ohjelmoijalla on parempi kontrolli mitä tehdään ja mitä tapahtuu suorituksen aikana async ja await käyttävät myös Task:ia Action hello = () => { Console.Write("Hello"); ; Action world = () => { Console.Write("World"); ; //Parallel.Invoke(hello, world); Task taskhello = Task.Factory.StartNew(hello); Task taskworld = Task.Factory.StartNew(world); Task.WaitAll(taskHello, taskworld); 17 Task - käynnistys Task sisältää propertynä TaskFactory-luokan, sen static-metodeilla käynnistetään taskit Käynnistyksessä parametrillä ohjataan toimintaa, esimerkiksi voidaan kertoa skedulerille että metodi kestää pitkään ja joutuu odottamaan välillä tai voidaan välittää CancellationToken Käynnistyksessä paluuarvona Task-luokka Sekä Action että Func delegaatit (siis paluuarvo on ok) Task.Factory.StartNew( () => Laskentaa(), TaskCreationOptions.LongRunning); var t = Task<string>.Factory.StartNew(() => 42.ToString()); t.wait(); this.title = t.result; 18
10 Tehtävien riippuvuudet toisista Miten seuraavan kaavion mukaiset toiminnot saadaan suoritettu rinnakkain? Nuolet osoittavat keskinäiset riippuvuudet eli metodien suoritysjärjestyksen 19 Task.Factory Parallel.Invoke ei yksin riitä, edellisen sivun esimerkki joko säikeillä tai monella Parallel.Invoke:lla tai Taskeilla: var f = Task.Factory; var build1 = f.startnew(() => Build(project1)); var build2 = f.startnew(() => Build(project2)); var build3 = f.startnew(() => Build(project3)); var build4 = f.continuewhenall(new[] { build1, _ => Build(project4)); var build5 = f.continuewhenall(new[] { build1, build2, build3, _ => Build(project5)); var build6 = f.continuewhenall(new[] { build3, build4, _ => Build(project6)); var build7 = f.continuewhenall(new[] { build5, build6, _ => Build(project7)); var build8 = f.continuewhenall(new[] { build5, _ => Build(project8)); Task.WaitAll(build1, build2, build3, build4, build5, build6, build7, build8); 20
11 Task odotus ja jatko Task:ia voidaan odottaa Wait-metodilla Monta taskia: odotetaan kaikkia tai ensimmäistä valmistunutta Jatkumo: kun taski on valmis niin saadaan automaattisesti käynnistymään seuraava metodi (task) var t = Task<string>.Factory.StartNew(() => 42.ToString()); t.continuewith(s=>trace.writeline("valmis", s.result)); var task1 = Task.Factory.StartNew(() => Lue()); var task2 = Task.Factory.StartNew(() => Laske()); var task3 = Task.Factory.StartNew(() => JokuMuu()); Task.WaitAll(task1, task2, task3); // tai Task.WaitAny(task1, task2, task3); Task.Factory.StartNew(() =>{ Thread.Sleep(3000);return "vastaus on 42";).ContinueWith(t => Console.WriteLine(t.Result)).ContinueWith(t => Console.WriteLine("Valmista on!")); 21 Task ja UI Task voi valita mitä skeduleria se käyttää, voidaan hyödyntää UI-ohjelmoinnossa (async/await vielä helpompi ratkaisu) //WPF-koodia: private void B_Click(object sender, RoutedEventArgs e) { button1.isenabled = false; // haetaan UI-säikeen konteksti käyttöön: var uischeduler = TaskScheduler.FromCurrentSynchronizationContext(); var t = Task<int>.Factory.StartNew(() => slowfunc(1, 2)); t.continuewith(x => { label1.content = "tulos: " + t.result.tostring(); button1.isenabled = true;, uischeduler ); 22
12 C#5: async ja await C#5 sisältää kieleen lisätyn piirteen, jolla voidaan toteuttaa entistä helpommin asynkronisia toimintoja. Käytännössä saadaan ajettua yhdellä säikeellä eri metodeja vuorotellen. async: esitellään metodi, jonka koodissa on await:lla tehtäviä asynkronisia metodikutsuja. Kääntäjä tuottaa koodia, joka odottaa await-metodin paluuta mutta sillä välin suorittaa async-metodin kutsujaa await: luovutetaan suoritusvuoro jollekin toiselle suorituskelpoiselle metodille 23 async (ja UI) esimerkki private async void B_Click(object sender, RoutedEventArgs e){ Title = "Haetaan uutisotsikoita..."; Otsikot = new ObservableCollection<string>( await haeotsikot ()); // B Click:n kutsuja pääsee suoritukseen sanomajonosta käsitellään // seuraavat sanomat käyttöliittymä pysyy toiminnassa // ja kun haeotsikot on valmis koodin suoritus jatkuu tästä Title = "Löytyi " + Otsikot.Count + " kpl otsikoita"; private Task<List<string>> haeotsikot () { Task<List<string>> t = new Task<List<string>>(() => { return uutispalvelu.haeotsikot(); ); t.start(); return t; 24
13 Web-sovellukset ja Taskit Jo.NET 1.0 sisälsi IHttpAsyncHandler rajapinnan ASP.NET MVC 2, 3, 4: AsyncController-luokka Asynkronisen kontrollerin tekeminen on helppoa async/await:in avulla Public class UutisiaController: AsyncController { public async Task<ActionResult> UutisetAsync(string syotenimi) { HakuToiminto.DataPalvelu data = new HakuToiminto.DataPalvelu(); return View("Uutiset", await syotteenuutiset(syotenimi)); // 25 AsyncController aikaisemmin public class HomeController : AsyncController { public void LongRunningActionAsync() { AsyncManager.OutstandingOperations.Increment(); Task.Factory.StartNew(() => DoLengthyOperation()); private void DoLengthyOperation() { Thread.Sleep(5000); AsyncManager.Parameters["message"] = "hello world"; AsyncManager.OutstandingOperations.Decrement(); public ActionResult LongRunningActionCompleted(string message) { return View(); 26
14 Milloin AsyncController? Jos Action-metodit ovat yksinkertaisia ja/tai CPU-intensiivisiä, käytä synkronista kontrolleria tai koodia ei haluta monimutkaistaa lainkaan HTTP-pyynnön suoritetaan ThreadPoolista otetun säikeen kontekstissa Asynkroniset kontrollerit/actionit mahdollistavat monen pyynnön yhtäaikaisen suorituksen samalla säikeellä parempi suorituskyky 27 Async ja Framework Task/Async toimintoja valmiina: Toiminto Web-toiminnot Tiedosto-IO Kuvien käsittely WCF Luokka käyttää async-metodeja HttpClient, SyndicationClient, Socket StorageFile, StreamWriter, StreamReader, XmlReader MediaCapture, BitmapEncoder, BitmapDecoder Synchronous and Asynchronous Operations try { string uri = "http://www.sovelto.fi"; string body = await client.getstringasync(uri); Console.WriteLine(body); catch (HttpRequestException ex) { Console.WriteLine("Virhe: {0", ex.message); 28
15 Task ja.net Task on merkittävä koska sitä käyttää varsin moni luokka/metodi Muita vaihtoehtoja on mm. Reactive Extensions (RX) http://msdn.microsoft.com/en-us/data/gg577609.aspx. The Reactive Extensions (Rx)......is a library to compose asynchronous and event-based programs using observable collections and LINQ-style query operators. 29 Synkronointi Muista huolehtia siitä että vain yksi säie/taski käsittelee kerrallaan mitä tahansa tietorakennetta Jopa kokonaislukumuuttujan käsittely täytyy tehdä oikein rinnakkaisessa koodissa Synkronointiluokkia on runsaasti sekä prosessien väliseen synkronointiin että prosessin sisällä eri säikeiden synkronointiin 30
16 System.Collections.Concurrent Säieturvalliset kokoelmaluokat, esim: ConcurrentBag<T> ConcurrentDictionary<TKey, TValue> ConcurrentQueue<T> ConcurrentStack<T> ConcurrentList<T> puuttuu koska sen toteuttaminen on säieturvallisesti erittäin vaikeaa Ohjelmoijalta jää pois säikeiden ja taskien välinen synkronointi kokoelmaluokkien käytössä ConcurrentDictionary<int, string> cd = new ConcurrentDictionary<int, string>(); cd.addorupdate(1, "eka", (j, k) => k + j.tostring()); bool ok = cd.tryadd(2, "kakkonen"); string s = cd[1]; ok = cd.trygetvalue(1, out s); 31 Windows 8 ja WinRT Windows 8 sisältää kaksi puolta: perinteinen UI ja Win32API kosketusnäytölle optimoitu WINRT WinRT:ssä kaikki mahdollisesti pitkäkestoiset toiminnot ovat asynkronisia joten rinnakkaisuuteen liittyvät asiat on pakko osata ja käyttää WinRT-sovelluksissa C#-koodaajan kannalta async/await samanlaista private async void Button_Click(object sender, RoutedEventArgs e) { SyndicationClient client = new SyndicationClient(); client.bypasscacheonretrieve = true; Uri feeduri = new Uri("http://www... feed = await client.retrievefeedasync(feeduri); feeddata feeddata = new feeddata(); items feeditem = null; foreach (SyndicationItem item in feed.items) //... 32
17 Debuggerin apuvälineitä Debuggerin ikkunat: Concurrency Visualizer (Analyze-valikossa) analyysi suorituksen rinnakkaisuudesta 33.NET-koodarin pakolliset taidot Generics-määrittelyt Extension-metodit Lambda-lausekkeet ja delegaatit, liittyvät kaikkeen asynkroniseen käsittelyyn Task-käsite async/await Synkronointimekanismit Tarvitaan sekä tekninen osaaminen että ymmärrys miksi ja miten asioita pitää tehdä 34
18 Aihealueen kursseja Soveltossa 20484 Essentials of Developing Windows Store Apps Using C#, 3 pv 25.3.2013 C# ja.net Framework -ohjelmointi, 4pv 18.3.2013 ASP.NET MVC, 3pv 22.5.2013 35 Thank you for coming! Feedback can be given via mobile or laptop through techdays.fi seminar schedule. 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, Windows Vista and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries. The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentations. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION. #td2013fi
19 Q&A t