CTU June 2011 - C# 5.0 - ASYNC & Await
Upcoming SlideShare
Loading in...5
×
 

CTU June 2011 - C# 5.0 - ASYNC & Await

on

  • 2,788 views

 

Statistics

Views

Total Views
2,788
Views on SlideShare
2,644
Embed Views
144

Actions

Likes
0
Downloads
44
Comments
0

6 Embeds 144

http://innovativesingapore.com 98
url_unknown 27
http://spiffy.sg 16
http://www.slideshare.net 1
http://www.spiffy.sg 1
http://spiffy1.capturepagedesigns.com 1

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • * Let's talk about this with waiters.* Read slowly: "A waiters job..."* Flaw in this. Can you see what it is? Obviously, a waiter can interleave!
  • [Joke: oh, you don’t want to go home early? Great. In that case I’ll dive into how it actually worked under the hood.]GOALS: To understand the “await” feature at a professional level -- that is, where the flow-of-control goes, which threads are involved, and how they communicate. Also to understand more deeply what asynchrony is. This will be enable you to be a better architect of asynchronous programs, e.g. Silverlight and ASP.This is the demo code, but I simplified it a little. I also split up “GetDiggAsync” and “await” onto separate lines. (Also I ported it over mostly to C#, apart from the XML literals).There are two existing threads allocated by the operating system. One is the UI thread for this process. The other is the “IO Completion Port” thread. Each of these threads has a queue associated with it.
  • When the user clicks a button, this inserts a “button-click-message” into the UI queue.The UI thread is in a while loop, checking for messages. When it gets this message it invokes the button-click handler.
  • The same thread makes function calls.When it calls the API “web.DownloadStringTaskAsync”, this returns immediately. It returns a Task, which I’ve called “downTask”. This task has not yet completed. The network stack will know to mark it as completed once the server’s response comes back.I should stress that “Task” does not mean a “BackgroundThreadTask”. The Task class is unrelated to questions of threads of execution.A “Task” is merely a “future” (C++), a “promise” – it’s an object that exists in one of three states, “InProgress” or “Completed(with result)” or “Faulted(with exception)”. It can be caused to transition from the first state to either of the other two. When this transition takes place, it will invoke any continuations that have been registered with it.And Task is a great unifying abstraction. That’s because it can stand for so many things – for a background worker thread on the current machine, or for a thread of execution on some remote database server, or for things that don’t take any threads at all like a button-click or a DMA transfer from disk to memory.Actually, if you’re familiar with the TPL, Task has a third state “Cancelled”. Through APIs we end up treating this state as equivalent to Faulted(with OperationCancelledException).
  • Now we execute the “await” operator. This does two things.First, it signs up a continuation onto downTask. For now I’ve written the continuation as “ui.Post{K1}” – not in real syntax. We’ll see later what it does. (Note that a task is allowed to have many continuations signed up on it.)Next, the first time we execute the “await” operator in an async method, we return a Task immediately. Once again, this task has not yet completed.
  • We execute the “await” operator. once again, this signs up a continuation onto the task, and returns to the calling thread (the UI thread).The UI thread can now resume it’s “while”-loop, checking for messages.If any other button-clicks happened (or mouse-drags or repaints or window-resizing) now, then they could be dealt with by the UI thread fine. This is where responsiveness comes in. (but it also brings in re-entrancy... imagine if the user clicked the same button again! then we’d start a second concurrent run through this button1_Click handler!)
  • Hey! A few seconds later, and the web services has delivered its answer to the IO Completion Port thread!
  • The IOCP thread knows which task was associated with that response.So it transitions it from the “Running” state to the “Completed with result ‘rss’” state. This causes it to execute the task’s continuation.
  • The continuation merely adds a message into the UI queue. Then it returns, allowing the IO Completion Port thread to resume its work.
  • The UI thread picks up the message from its queue, and responds by dispatching to K1, which assigns into the variable “rss”.
  • The code continues to execute. it comes to the “return” statement.(note that we have already returned the Task “diggTask” from this method. So you know the return statement is going to do something different...)
  • The “return” statement, in an async method, sets its returned task’s state to “Completed”, provides a result, and executes the task’s continuation.Once again, the continuation merely posts to the UI’s message-queue.
  • The method returns. Now the UI-thread can go back to its “while” loop, checking for messages in the queue. (There is one already!)
  • And the UI thread executes the method, puts the story into the text-box, and finishes.Note that ALL user code executed on the UI thread. All of it. That means the user never had to worry about the typical multi-threaded problems (semaphores, mutexes, semaphores, races, locks, ...)Also count how many threads were involved. Just the two that were already provided by the operating system. We didn’t create ANY additional threads.
  • These are some of the “Task Async Pattern” APIs that are included in the CTP.
  • We initially hadvar task2 = task.TimeoutAfter(1000);But we removed it because it didn’t have a clear-enough design. Would you want task2 to end with an OperationCancelledException after 1000ms? Or to end successfully? Both forms are useful. In the end, we figured that cancellation-after-1000ms was easier done like this:varcts = new CancellationTokenSource();cts.CancelAfter(1000)And we figured that successful-termination-after-1000ms was clearer if you wrote it out manually:var task2 = task.WhenAny(task, Task.Delay(1000))
  • There are three kinds of async methods. The difference between them is subtle.The difference between them is so subtle that we considered eliminating the first kind “void-returning asyncs” entirely. But that would have been wrong. That’s because every single async method you write will be invoked by someone who is also async, all the way up to the very top level of the callstack, to the “fire-and-forget” event handlers at the top like Button1_Click().So: every program that you’ll ever write will use void-returning asyncs. We have to accept that, and include it as a language feature.
  • Something to note here is that Task is composable.For instance, you can write a method which takes any two tasks and awaits until they’re both done.You can’t do that with the APM or the EAP. That’s because APM is just “a pair of methods with arbitrary parameters” and WebClient is just “a set of methods and events you have to call”. They’re not first-class citizens. They’re not things that you can pass as parameters to another method. But you can pass a Task directly to another method.That’s why the TAP is better.

CTU June 2011 - C# 5.0 - ASYNC & Await CTU June 2011 - C# 5.0 - ASYNC & Await Presentation Transcript

  • C# 5 async & await
    Justin Lee
    Software Development Consultant
    Community Technology Update 2011
    25th June 2011
  • Concurrency is…
    about running or appearing to run two things at once, through cooperative or pre-emptive multitasking or multicore.
    Good reasons to use concurrency:
    for the CPU-bound multicore computational kernel (e.g. codecs);
    for a server handling requests from different processes/machines;
    to “bet on more than one horse” and use whichever was fastest.
  • Asynchrony is…
    about results that are delayed, and yielding control while awaiting them (co-operative multitasking).
    Good reasons to use asynchrony:
    for overall control / coordination structure of a program;
    for UI responsiveness;
    for IO- and network-bound code;
    for coordinating your CPU-bound multicore computational kernel.
  • “A waiter’s job is to wait on a table until the patrons have finished their meal.If you want to serve two tables concurrently, you must hire two waiters.”
  • Demo
    Converting from synchronous, to asynchronous, to using await
  • UIthread
    IOCP
    thread
    asyncTask<string> LoadMoviesAsync()
    {var web = newWebClient();
    vardTask = web.DownTaskAsync("http://netflix.com");
    varrss = awaitdTask ;
    var movies = XElement.Parse(rss).<story>.<description>;
    return movies;
    }
    async voidSearchButtonClick()
    {vartask = QueryMoviesAsync();
    varm = awaittask;
    textBox1.Text = movies;
    }
    How the demo actually worked
  • UIthread
    [1/12] A button-click arrives on the UI queue
    IOCP
    thread
    async voidSearchButtonClick()
    {vartask = QueryMoviesAsync();
    varm = awaittask;
    textBox1.Text = movies;
    }
    asyncTask<string> LoadMoviesAsync()
    {var web = newWebClient();
    vardTask = web.DownTaskAsync("http://netflix.com");
    varrss = awaitdTask ;
    var movies = XElement.Parse(rss).<story>.<description>;
    return movies;
    }
    Click
  • UIthread
    [2/12] Invoke some functions; get back “dTask” from the API
    IOCP
    thread
    async voidSearchButtonClick()
    {vartask = QueryMoviesAsync();
    varm = awaittask;
    textBox1.Text = movies;
    }
    asyncTask<string> LoadMoviesAsync()
    {var web = newWebClient();
    vardTask = web.DownTaskAsync("http://netflix.com");
    varrss = awaitdTask ;
    var movies = XElement.Parse(rss).<story>.<description>;
    return movies;
    }
    Click
    dTask
  • UIthread
    [3/12] “await task” assigns a continuation and returns task
    IOCP
    thread
    asyncTask<string> LoadMoviesAsync()
    {var web = newWebClient();
    var task = web.DownTaskAsync("http://netflix.com");
    varrss = await task;
    var movies = XElement.Parse(rss).<story>.<description>;
    return movies;
    }
    async voidSearchButtonClick()
    {vartask = QueryMoviesAsync();
    varm = awaittask;
    textBox1.Text = movies;
    }
    Click
    task
    dTask
    dTask»ui.Post{Κ1}
    Κ1:
  • UIthread
    [4/12] “await task” assigns a continuation and returns
    IOCP
    thread
    async voidSearchButtonClick()
    {vartask = QueryMoviesAsync();
    varm = awaittask;
    textBox1.Text = movies;
    }
    asyncTask<string> LoadMoviesAsync()
    {var web = newWebClient();
    vardTask = web.DownTaskAsync("http://netflix.com");
    varrss = awaitdTask ;
    var movies = XElement.Parse(rss).<story>.<description>;
    return movies;
    }
    Click
    task
    task »ui.Post{Κ2}
    dTask»ui.Post{Κ1}
    Κ1:
    Κ2:
  • UIthread
    [5/12] Network packet arrives with data
    IOCP
    thread
    async voidSearchButtonClick()
    {vartask = QueryMoviesAsync();
    varm = awaittask;
    textBox1.Text = movies;
    }
    asyncTask<string> LoadMoviesAsync()
    {var web = newWebClient();
    vardTask = web.DownTaskAsync("http://netflix.com");
    varrss = awaitdTask ;
    var movies = XElement.Parse(rss).<story>.<description>;
    return movies;
    }
    Click
    task »ui.Post{Κ2}
    dTask»ui.Post{Κ1}
    rss
    Κ1:
    Κ2:
  • UIthread
    [6/12] Invoke dTask’scontinuation with that data
    IOCP
    thread
    async voidSearchButtonClick()
    {vartask = QueryMoviesAsync();
    varm = awaittask;
    textBox1.Text = movies;
    }
    asyncTask<string> LoadMoviesAsync()
    {var web = newWebClient();
    vardTask = web.DownTaskAsync("http://netflix.com");
    varrss = awaitdTask ;
    var movies = XElement.Parse(rss).<story>.<description>;
    return movies;
    }
    Click
    task »ui.Post{Κ2}
    dTask»ui.Post{Κ1}
    rss
    ui.Post{Κ1(rss)}
    Κ1:
    Κ2:
  • UIthread
    [7/12] Continuation is a “Post”, i.e. addition to the UI queue
    IOCP
    thread
    async voidSearchButtonClick()
    {vartask = QueryMoviesAsync();
    varm = awaittask;
    textBox1.Text = movies;
    }
    asyncTask<string> LoadMoviesAsync()
    {var web = newWebClient();
    vardTask = web.DownTaskAsync("http://netflix.com");
    varrss = awaitdTask ;
    var movies = XElement.Parse(rss).<story>.<description>;
    return movies;
    }
    Click
    task »ui.Post{Κ2}
    rss
    ui.Post{Κ1(rss)}
    Κ1:
    K1(rss)
    Κ2:
  • UIthread
    [8/12] UI thread executes K1, giving a result to the “await”
    IOCP
    thread
    async voidSearchButtonClick()
    {vartask = QueryMoviesAsync();
    varm = awaittask;
    textBox1.Text = movies;
    }
    asyncTask<string> LoadMoviesAsync()
    {var web = newWebClient();
    vardTask = web.DownTaskAsync("http://netflix.com");
    varrss = awaitdTask ;
    var movies = XElement.Parse(rss).<story>.<description>;
    return movies;
    }
    Click
    task »ui.Post{Κ2}
    rss
    ui.Post{Κ1(rss)}
    Κ1:
    K1(rss)
    Κ2:
  • UIthread
    [9/12] “return movies” will signal completion of task
    IOCP
    thread
    async voidSearchButtonClick()
    {vartask = QueryMoviesAsync();
    varm = awaittask;
    textBox1.Text = movies;
    }
    asyncTask<string> LoadMoviesAsync()
    {var web = newWebClient();
    vardTask = web.DownTaskAsync("http://netflix.com");
    varrss = awaitdTask ;
    var movies = XElement.Parse(rss).<story>.<description>;
    return movies;
    }
    Click
    task »ui.Post{Κ2}
    rss
    ui.Post{Κ1(rss)}
    Κ1:
    K1(rss)
    Κ2:
  • UIthread
    [10/12] Invoke task’s continuation with data
    (by posting to UI queue)
    IOCP
    thread
    async voidSearchButtonClick()
    {vartask = QueryMoviesAsync();
    varm = awaittask;
    textBox1.Text = movies;
    }
    asyncTask<string> LoadMoviesAsync()
    {var web = newWebClient();
    vardTask = web.DownTaskAsync("http://netflix.com");
    varrss = awaitdTask ;
    var movies = XElement.Parse(rss).<story>.<description>;
    return movies;
    }
    Click
    task »ui.Post{Κ2}
    rss
    ui.Post{Κ1(rss)}
    Κ1:
    K1(rss)
    ui.Post(Κ2(movie))
    Κ2:
    K2(movie)
  • UIthread
    [11/12] Return from handling the K1 continuation
    IOCP
    thread
    async voidSearchButtonClick()
    {vartask = QueryMoviesAsync();
    varm = awaittask;
    textBox1.Text = movies;
    }
    asyncTask<string> LoadMoviesAsync()
    {var web = newWebClient();
    vardTask = web.DownTaskAsync("http://netflix.com");
    varrss = awaitdTask ;
    var movies = XElement.Parse(rss).<story>.<description>;
    return movies;
    }
    Click
    rss
    ui.Post{Κ1(rss)}
    Κ1:
    K1(rss)
    ui.Post(Κ2(movie))
    Κ2:
    K2(movie)
  • UIthread
    [12/12] UI thread executes K2, giving a result to the “await”
    IOCP
    thread
    async voidSearchButtonClick()
    {vartask = QueryMoviesAsync();
    varm = awaittask;
    textBox1.Text = movies;
    }
    asyncTask<string> LoadMoviesAsync()
    {var web = newWebClient();
    vardTask = web.DownTaskAsync("http://netflix.com");
    varrss = awaitdTask ;
    var movies = XElement.Parse(rss).<story>.<description>;
    return movies;
    }
    Click
    rss
    ui.Post{Κ1(rss)}
    Κ1:
    K1(rss)
    ui.Post(Κ2(movie))
    Κ2:
    K2(movie)
  • Demo
    Using await and async with Silverlight
  • // network
    strings = awaitwebClient.DownloadStringTaskAsync("http://a.com");
    strings = awaitwebClient.UploadStringTaskAsync(newUri("http://b"), "dat");
    awaitWebRequest.Create("http://a.com").GetResponseAsync();
    awaitsocket.ConnectAsync("a.com",80);
    awaitworkflowApplication.RunAsync();
    awaitworkflowApplication.PersistAsync();
    PingReply r = awaitping.SendTaskAsync("a.com");
    // stream
    strings = awaittextReader.ReadToEndAsync();
    awaitstream.WriteAsync(buffer, 0, 1024);
    awaitstream.CopyToAsync(stream2);
    // UI
    awaitpictureBox.LoadTaskAsync("http://a.com/pic.jpg");
    awaitsoundPlayer.LoadTaskAsync();
    // task/await, assuming “task” of type IEnumerable<Task<T>>
    T[] results = awaitTaskEx.WhenAll(tasks);
    Task<T>winner = awaitTaskEx.WhenAny(tasks);
    Task<T> task = TaskEx.Run(delegate {... return x;});
    awaitTaskEx.Delay(100);
    awaitTaskEx.Yield();
    awaitTaskScheduler.SwitchTo();
    awaitDispatcher.SwitchTo();
    Ultimately the contents of TaskEx will be moved into Task.
    How to use the “Task Async Pattern” [TAP]
  • classForm1 : Form
    {
    privatevoidbtnGo_Click(object sender, EventArgs e)
    {
    cts = newCancellationTokenSource();
    cts.CancelAfter(5000);
    try
    {
    awaitnewWebClient().DownloadStringTaskAsync(newUri("http://a.com"), cts.Token);
    awaitnewWebClient().DownloadStringTaskAsync(newUri("http://b.com"), cts.Token);
    }
    catch (OperationCancelledException) { ... }
    finally {cts= null;}
    }
    CancellationTokenSourcects;
    privatevoidbtnCancel_Click(object sender, EventArgs e)
    {
    if (cts!=null) cts.Cancel();
    }
    }
    This is the proposed new standard framework pattern for cancellation.
    Note that cancellation token is able to cancel the current operation in an async sequence; or it can cancel several concurrent async operations;
    or you can take it as a parameter in your own async methods and pass it on to sub-methods. It is a “composable” way of doing cancellation.
    How to use TAP cancellation
  • classForm1 : Form
    {
    privatevoidbtnGo_Click(object sender, EventArgs e)
    {
    varcts = newCancellationTokenSource();
    cts.CancelAfter(5000);
    btnCancel.Click += cts.EventHandler;
    try
    {
    // a slicker, more local way to handle cancellation...
    awaitnewWebClient().DownloadStringTaskAsync(newUri("http://a.com"), cts.Token);
    awaitnewWebClient().DownloadStringTaskAsync(newUri("http://b.com"), cts.Token);
    }
    catch (OperationCancelledException) { ... }
    finally{btnCancel.Click -= cts.EventHandler;}
    }
    }
    publicstaticclassExtensions
    { publicstaticvoidEventHandler(thisCancellationTokenSourcects, object_, EventArgs e)
    { cts.Cancel(); }
    }
    In this version, we keep “cts” local to just the operation it controls.
    Note that “cts” can’t be re-used: once it has been cancelled, it remains cancelled. That’s why we create a new one each time the user clicks “Go”.
    A good idea: btnGo.Enabled=false; btnCancel.Enabled=true;
    How to use TAP cancellation [advanced]
  • privatevoid btnGo_Click(object sender, EventArgs e)
    {
    var progress = newEventProgress<DownloadProgressChangedEventArgs>();
    // Set up a progress-event-handler (which will always fire on the UI thread,
    // even if we'd launched the task ona different thread).
    progress.ProgressChanged += (_, ee) =>
    {
    progressBar1.Value = ee.Value.ProgressPercentage;
    };
    // Wait for the task to finish
    awaitnewWebClient().DownloadStringTaskAsync(uri, cts.Token, progress);
    }
    interfaceIProgress<T>
    { voidReport(T value);
    }
    This is the proposed new standard framework pattern for progress-reporting (for those APIs that support progress-reporting).
    • The user passes in a “progress” parameter
    • This parameter is EventProgress<T>, or any other class that implements IProgress<T>... (it’s up to the consumer how to deal with progress)
    How to use TAP progress
  • // handle progress with a "while" loop, instead of a callback:
    varprogress = newLatestProgress<DownloadProgressChangedEventArgs>();
    vartask = newWebClient().DownloadStringTaskAsync(uri, cts.Token, progress);
    while(await progress.Progress(task))
    {
    progressBar1.Value = progress.Latest.ProgressPercentage;
    }
    // another “while” loop, except this one queues up reports so we don’t lose any:
    varprogress = newQueuedProgress<DownloadProgressChangedEventArgs>();
    vartask = newWebClient().DownloadStringTaskAsync(uri, cts.Token, progress);
    while(await progress.NextProgress(task))
    {
    progressBar1.Value = progress.Current.ProgressPercentage;
    }
    • PUSH techniques are ones where the task invokes a callback/handler whenever the task wants to – e.g. EventProgress, IObservable.
    • PULL techniques are ones where UI thread choses when it wants to pull the next report – e.g. LatestProgress, QueuedProgress.
    • The classes LatestProgresss and QueuedProgress are in the “ProgressAndCancellation” sample in the CTP.
    How to use TAP progress [advanced]
  • Task<string[]> GetAllAsync(Uri[] uris, CancellationTokencancel, IProgress<int> progress)
    {
    var results = newstring[uris.Length];
    for (int i=0; i<uris.Length; i++)
    {
    cancel.ThrowIfCancellationRequested();
    results[i] = awaitnewWebClient().DownloadStringTaskAsync(uris[i], cancel);
    if (progress!=null) progress.Report(i);
    }
    return results;
    }
    Take Cancel/progress parameters: If your API supports both cancellation and progress, add a single overload which takes both. If it supports just one, add a single overload which takes it.
    Listen for cancellation: either do the pull technique of “cancel.ThrowIfCancellationRequested()” in your inner loop, or the push technique of “cancel.Register(Action)” to be notified of cancellation, or...
    Pass cancellation down: usually it will be appropriate to pass the cancellation down to nested async functions that you call.
    Report progress: in your inner loop, as often as makes sense, report progress. The argument to progress.Report(i) may be read from a different thread, so make sure it’s either read-only or threadsafe.
    How to implement TAP cancellation/progress
  • Task Delay(intms, CancellationTokencancel);
    Task<T> Run<T>(Func<T> function);
    Task<IEnumerable<T>> WhenAll<T>(IEnumerable<Task<T>> tasks);
    Task<Task<T>> WhenAny<T>(IEnumerable<Task<T>> tasks);
    // WhenAny is like Select. When you await it, you get the task that “won”.
    // WhenAll over a LINQ query
    int[] results = awaitTaskEx.WhenAll(fromurlinurlsselectGetIntAsync(url));
    // WhenAny to implement a concurrent worker pool
    Queue<string> todo= ...;
    var workers = newHashSet<Task<int>>();
    for(inti=0; i<10; i++) workers.Add(GetIntAsync(todo.Dequeue());
    while (workers.Count>0)
    {
    var winner = awaitTaskEx.WhenAny(workers);
    Console.WriteLine(await winner);
    workers.Remove(winner);
    if (todo.Count>0) workers.Add(GetIntAsync(todo.Dequeue());
    }
    Task<T> combinators
  • asyncvoidFireAndForgetAsync() {
    await t;
    }
    asyncTask MerelySignalCompletionAsync() {
    return;
    }
    AsyncTask<int> GiveResultAsync() {
    return 15;
    }
    FireAndForgetAsync();
    awaitMerelySignalCompletionAsync();
    varr = awaitGiveResultAsync();
    Async subs (“void-returning asyncs”): used for “fire-and-forget” scenarios. Control will return to the caller after the first Await. But once “t” has finished, the continuation will be posted to the current synchronization context. Any exceptions will be thrown on that context.
    Task-returning asyncs: Used if you merely want to know when the task has finished. Exceptions get squirrelled away inside the resultant Task.
    Task(Of T)-returning asyncs: Used if you want to know the result as well.
    Three kinds of asyncmethod

  • // Task Asynchronous Pattern [TAP], with Cancellation and Progress
    Task<TR> GetStringAsync(Params..., [CancellationTokenCancel], [IProgress<TP>Progress])

    // Asynchronous Programming Model [APM]IAsyncResultBeginGetString(Params..., AsyncCallbackCallback, object state);TR EndGetString(IAsyncResult);

    // Event-based Asynchronous Pattern [EAP]classC
    {
    publicvoidGetStringAsync(Params...);
    publiceventGetStringCompletedEventHandlerGetStringCompleted;
    publicvoidCancelAsync();
    }
    classGetStringCompletedEventArgs
    {
    publicTR Result { get; }
    publicException Error { get; }
    }
    Comparing TAP to its predecessors
  • Demo
    Progress and Cancellation
  • Demo
    Async on Windows Phone 7
    (if there’s time)
  • Q & A
    triplez@justinlee.sg