Demystifying the .NET Asynchronous Programming Landscape Bart J.F. De Smet blogs.bartdesmet.net/bart bartde@microsoft.com
It’s All About Time! What’s asynchrony? Greek origin a (not) syn (together with) chronos (time) Multiple parties evolving in time independently Not being blocked Related concepts Concurrency Order in which multiple tasks execute is not determined Parallelism True simultaneous execution (e.g. multicore)
It’s a Jungle Out There! Your Program Here Reactive Events triggered Packet received Your Program Here completed I/O Callbacks Events
Nobody Likes to be Blocked A connected world with I/O Network sockets Web services Instant messaging Don’t block the UI thread! PostMessage Control.Invoke Dispatcher.BeginInvoke Asynchrony in frameworks Silverlight Windows Phone 7 AJAX Language support?
Hardware based on asynchrony… From the Bottom Up Hardware based on asynchrony… Programming model?
Abstractions in the .NET Framework Spot the defects using System; using System.IO; class Program { static void Main() var buffer = new byte[4 * 1024 * 1024 /* 4MB */]; using (var fs = File.OpenRead(@“c:\temp\sample.big”)) fs.BeginRead(buffer, 0, buffer.Length, iar => // Process results in buffer }, null); } Lifetime issues? Callback using a lambda expression Lack of closures in C# 1.0 Need EndRead?
Abstractions in the .NET Framework Asynchronous Methods Event-Based Asynchronous Pattern BackgroundWorker Component IAsyncResult BeginRead(…, AsyncCallback callback, object state); int EndRead(IAsyncResult result); Use of callback functions void DownloadAsync(…); event DownloadCompletedEventHandler DownloadCompleted; void RunWorkerAsync(); void ReportProgress(int percentProgress); event DoWorkEventHandler DoWork; event ProgressChangedEventHandler ProgressChanged; event RunWorkerCompletedEventHandler RunWorkerCompleted; Asynchronous is not unlike event-driven
Asynchronous Methods in .NET
The Task Parallel Library in .NET 4 True parallelism has become reality Can you keep the cores busy? Gordon Moore
Essential TPL Concepts – Task<T> Representation of a computation Also known as a “future” Cheaper than threads Continuations can be hooked up Also known as “continuation passing style” (CPS) Code runs on the CLR’s task pool Task<int> x = Task<int>.Factory.StartNew(() => { // (Long-running) computation of the result. }); x.ContinueWith(x2 => { // Code for the continuation after x completes, // with x2 an alias for the original task. }); Continuations allow for sequencing Your application logic ends up inside out
Continuations for Dummies Returns a byte[] var report = Task.Factory.StartNew(() => GetFileData()) Receives a Task<byte[]> Data available in byte[] Data Flow Pipeline What about errors? .ContinueWith(x => Analyze(x.Result)) Returns a double[] Receives a Task<double[]> .ContinueWith(y => Summarize(y.Result));
Working with Task<T> in .NET 4
Language Support for Asynchronous Programming – F# let processAsync i = async { use stream = File.OpenRead(sprintf "Image%d.tmp" i) let! pixels = stream.AsyncRead(numPixels) let pixels' = transform pixels i use out = File.OpenWrite(sprintf "Image%d.done" i) do! out.AsyncWrite(pixels') } let processAsyncDemo = printfn "async demo..." let tasks = [ for i in 1 .. numImages -> processAsync i ] Async.RunSynchronously (Async.Parallel tasks) |> ignore printfn "Done!" Compiler Transformation stream.Read(numPixels, pixels -> let pixels' = transform pixels i use out = File.OpenWrite(sprintf "Image%d.done" i) do! out.AsyncWrite(pixels') )
(Asynchronous) Workflows in F# General-purpose language feature introduced by F# Based on theory of monads More exhaustive compared to LINQ in C# and VB Overloadable meaning for specific keywords Continuation passing style Synchronous: ‘a -> ‘b Asynchronous: ‘a -> (‘b -> unit) -> unit In C# style: Action<T, Action<R>> Core concept: async { /* code */ } Syntactic sugar for keywords inside block E.g. let!, do!, use! Continuation function receives result (~ ContinueWith)
Asynchronous Workflows in F#
Introducing the Visual Studio Async CTP PDC 2010
Asynchronous Control Flow async void DoWorkAsync() { var t1 = ProcessFeedAsync("www.acme.com/rss"); var t2 = ProcessFeedAsync("www.xyznews.com/rss"); await Task.WhenAll(t1, t2); DisplayMessage("Done"); } async Task ProcessFeedAsync(string url) { var text = await DownloadFeedAsync(url); var doc = ParseFeedIntoDoc(text); await SaveDocAsync(doc); ProcessLog.WriteEntry(url); } Message Pump UI Thread
Asynchronous Control Flow async void DoWorkAsync() { var t1 = ProcessFeedAsync("www.acme.com/rss"); var t2 = ProcessFeedAsync("www.xyznews.com/rss"); await Task.WhenAll(t1, t2); DisplayMessage("Done"); } async Task ProcessFeedAsync(string url) { var text = await DownloadFeedAsync(url); var doc = ParseFeedIntoDoc(text); await SaveDocAsync(doc); ProcessLog.WriteEntry(url); }
Asynchronous Control Flow async void DoWorkAsync() { var t1 = ProcessFeedAsync("www.acme.com/rss"); var t2 = ProcessFeedAsync("www.xyznews.com/rss"); await Task.WhenAll(t1, t2); DisplayMessage("Done"); } async Task ProcessFeedAsync(string url) { var text = await DownloadFeedAsync(url); var doc = ParseFeedIntoDoc(text); await SaveDocAsync(doc); ProcessLog.WriteEntry(url); }
Asynchronous Control Flow async void DoWorkAsync() { var t1 = ProcessFeedAsync("www.acme.com/rss"); var t2 = ProcessFeedAsync("www.xyznews.com/rss"); await Task.WhenAll(t1, t2); DisplayMessage("Done"); } async Task ProcessFeedAsync(string url) { var text = await DownloadFeedAsync(url); var doc = ParseFeedIntoDoc(text); await SaveDocAsync(doc); ProcessLog.WriteEntry(url); }
Asynchronous Control Flow async void DoWorkAsync() { var t1 = ProcessFeedAsync("www.acme.com/rss"); var t2 = ProcessFeedAsync("www.xyznews.com/rss"); await Task.WhenAll(t1, t2); DisplayMessage("Done"); } async Task ProcessFeedAsync(string url) { var text = await DownloadFeedAsync(url); var doc = ParseFeedIntoDoc(text); await SaveDocAsync(doc); ProcessLog.WriteEntry(url); } t1
Asynchronous Control Flow async void DoWorkAsync() { var t1 = ProcessFeedAsync("www.acme.com/rss"); var t2 = ProcessFeedAsync("www.xyznews.com/rss"); await Task.WhenAll(t1, t2); DisplayMessage("Done"); } async Task ProcessFeedAsync(string url) { var text = await DownloadFeedAsync(url); var doc = ParseFeedIntoDoc(text); await SaveDocAsync(doc); ProcessLog.WriteEntry(url); } t1 t2
Asynchronous Control Flow async void DoWorkAsync() { var t1 = ProcessFeedAsync("www.acme.com/rss"); var t2 = ProcessFeedAsync("www.xyznews.com/rss"); await Task.WhenAll(t1, t2); DisplayMessage("Done"); } async Task ProcessFeedAsync(string url) { var text = await DownloadFeedAsync(url); var doc = ParseFeedIntoDoc(text); await SaveDocAsync(doc); ProcessLog.WriteEntry(url); } t1 t2
Asynchronous Control Flow async void DoWorkAsync() { var t1 = ProcessFeedAsync("www.acme.com/rss"); var t2 = ProcessFeedAsync("www.xyznews.com/rss"); await Task.WhenAll(t1, t2); DisplayMessage("Done"); } async Task ProcessFeedAsync(string url) { var text = await DownloadFeedAsync(url); var doc = ParseFeedIntoDoc(text); await SaveDocAsync(doc); ProcessLog.WriteEntry(url); } t1 t2
Asynchronous Control Flow async void DoWorkAsync() { var t1 = ProcessFeedAsync("www.acme.com/rss"); var t2 = ProcessFeedAsync("www.xyznews.com/rss"); await Task.WhenAll(t1, t2); DisplayMessage("Done"); } async Task ProcessFeedAsync(string url) { var text = await DownloadFeedAsync(url); var doc = ParseFeedIntoDoc(text); await SaveDocAsync(doc); ProcessLog.WriteEntry(url); } t1 t2
Asynchronous Control Flow async void DoWorkAsync() { var t1 = ProcessFeedAsync("www.acme.com/rss"); var t2 = ProcessFeedAsync("www.xyznews.com/rss"); await Task.WhenAll(t1, t2); DisplayMessage("Done"); } async Task ProcessFeedAsync(string url) { var text = await DownloadFeedAsync(url); var doc = ParseFeedIntoDoc(text); await SaveDocAsync(doc); ProcessLog.WriteEntry(url); } t1 t2
Asynchronous Control Flow async void DoWorkAsync() { var t1 = ProcessFeedAsync("www.acme.com/rss"); var t2 = ProcessFeedAsync("www.xyznews.com/rss"); await Task.WhenAll(t1, t2); DisplayMessage("Done"); } async Task ProcessFeedAsync(string url) { var text = await DownloadFeedAsync(url); var doc = ParseFeedIntoDoc(text); await SaveDocAsync(doc); ProcessLog.WriteEntry(url); } t1 t2
Asynchronous Control Flow async void DoWorkAsync() { var t1 = ProcessFeedAsync("www.acme.com/rss"); var t2 = ProcessFeedAsync("www.xyznews.com/rss"); await Task.WhenAll(t1, t2); DisplayMessage("Done"); } async Task ProcessFeedAsync(string url) { var text = await DownloadFeedAsync(url); var doc = ParseFeedIntoDoc(text); await SaveDocAsync(doc); ProcessLog.WriteEntry(url); } t1 t2
Asynchronous Control Flow async void DoWorkAsync() { var t1 = ProcessFeedAsync("www.acme.com/rss"); var t2 = ProcessFeedAsync("www.xyznews.com/rss"); await Task.WhenAll(t1, t2); DisplayMessage("Done"); } async Task ProcessFeedAsync(string url) { var text = await DownloadFeedAsync(url); var doc = ParseFeedIntoDoc(text); await SaveDocAsync(doc); ProcessLog.WriteEntry(url); } t1 t2
Asynchronous Control Flow async void DoWorkAsync() { var t1 = ProcessFeedAsync("www.acme.com/rss"); var t2 = ProcessFeedAsync("www.xyznews.com/rss"); await Task.WhenAll(t1, t2); DisplayMessage("Done"); } async Task ProcessFeedAsync(string url) { var text = await DownloadFeedAsync(url); var doc = ParseFeedIntoDoc(text); await SaveDocAsync(doc); ProcessLog.WriteEntry(url); } t1 t2
Asynchronous Control Flow async void DoWorkAsync() { var t1 = ProcessFeedAsync("www.acme.com/rss"); var t2 = ProcessFeedAsync("www.xyznews.com/rss"); await Task.WhenAll(t1, t2); DisplayMessage("Done"); } async Task ProcessFeedAsync(string url) { var text = await DownloadFeedAsync(url); var doc = ParseFeedIntoDoc(text); await SaveDocAsync(doc); ProcessLog.WriteEntry(url); } t1 t2
Asynchronous Control Flow async void DoWorkAsync() { var t1 = ProcessFeedAsync("www.acme.com/rss"); var t2 = ProcessFeedAsync("www.xyznews.com/rss"); await Task.WhenAll(t1, t2); DisplayMessage("Done"); } async Task ProcessFeedAsync(string url) { var text = await DownloadFeedAsync(url); var doc = ParseFeedIntoDoc(text); await SaveDocAsync(doc); ProcessLog.WriteEntry(url); } t1 t2
Asynchronous Control Flow async void DoWorkAsync() { var t1 = ProcessFeedAsync("www.acme.com/rss"); var t2 = ProcessFeedAsync("www.xyznews.com/rss"); await Task.WhenAll(t1, t2); DisplayMessage("Done"); } async Task ProcessFeedAsync(string url) { var text = await DownloadFeedAsync(url); var doc = ParseFeedIntoDoc(text); await SaveDocAsync(doc); ProcessLog.WriteEntry(url); } t1 t2
Visual Studio Async CTP
Events Are All Around Us GPS RSS feeds Stock tickers Social media UI events Server management
Event Processing Systems Way simpler with Rx (𝑓 ◦ 𝑔)(𝑥) = 𝑓(𝑔(𝑥)) Rx is a library for composing asynchronous and event-based programs using observable sequences. Queries! LINQ! .NET 3.5 SP1 and 4.0 Silverlight 3, 4, and 5 XNA 4.0 for Xbox 360 Windows Phone 7 JavaScript (RxJS) Download at MSDN Data Developer Center or use NuGet
Push-Based Data Retrieval 4/15/2017 3:17 AM Push-Based Data Retrieval Application MoveNext OnNext Got next? Interactive Have next! Reactive Environment IEnumerable<T> IEnumerator<T> IObservable<T> IObserver<T> © 2007 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 presentation. 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.
Event Programming Interfaces Both interfaces ship in the .NET 4 BCL interface IObservable<out T> { IDisposable Subscribe(IObserver<T> observer); } interface IObserver<in T> void OnNext(T value); void OnError(Exception ex); void OnCompleted(); } Observers used to define callbacks In today’s word, errors are a given
Writing Rx Queries over Event Streams Importing .NET events as observables // IObservable<string> from TextChanged events var changed = Observable.FromEventPattern(txt, "TextChanged"); var input = (from text in changed select ((TextBox)text.Sender).Text); .DistinctUntilChanged() .Throttle(TimeSpan.FromSeconds(1)); // Bridge with the dictionary web service var svc = new DictServiceSoapClient(); var lookup = Observable.FromAsyncPattern<string, DictionaryWord[]> (svc.BeginLookup, svc.EndLookup); // Retrieve results and hop from stream to stream var res = (from term in input select lookup(term)) .Switch(); Bridging the async method pattern One stream per web service request
Event Processing with Rx
Asynchronous Data Processing Overview # IEnumerable<T> IObservable<T> res = from x in xs from y in q(x) …; Multiple values (*) Functional style code foreach (var z in res) … res.Subscribe(x => … Composition on query expressions Func<T> Task<T> y = f(x); y = await g(x); Imperative style code Single value (1) Invocation expressions Await expressions Sequencing through statements Synchronous Asynchronous
Summary Asynchrony is everywhere Emergence of async-only APIs Windows Phone, Silverlight, JavaScript Don’t block the UI Have to deal with latency Call to action Learn about Task<T> Download the Async CTP http://msdn.com/vstudio/async Download Reactive Extensions (Rx) http://msdn.com/data/gg577609
Stay up to date with MSDN Belux Register for our newsletters and stay up to date: http://www.msdn-newsletters.be Technical updates Event announcements and registration Top downloads Follow our blog http://blogs.msdn.com/belux Join us on Facebook http://www.facebook.com/msdnbe http://www.facebook.com/msdnbelux LinkedIn: http://linkd.in/msdnbelux/ Twitter: @msdnbelux Download MSDN/TechNet Desktop Gadget http://bit.ly/msdntngadget Please keep this slide
TechDays 2011 On-Demand Watch this session on-demand via Channel9 http://channel9.msdn.com/belux Download to your favorite MP3 or video player Get access to slides and recommended resources by the speakers
THANK YOU