32
C# 5 async & await Justin Lee Software Development Consultant Community Technology Update 2011 25 th June 2011

CTU June 2011 - C# 5.0 - ASYNC & Await

  • Upload
    spiffy

  • View
    2.412

  • Download
    5

Embed Size (px)

Citation preview

Page 1: CTU June 2011 - C# 5.0 - ASYNC & Await

C# 5 async & await

Justin LeeSoftware Development ConsultantCommunity Technology Update 201125th June 2011

Page 2: CTU June 2011 - C# 5.0 - ASYNC & Await

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.

Page 3: CTU June 2011 - C# 5.0 - ASYNC & Await

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.

Page 4: CTU June 2011 - C# 5.0 - ASYNC & Await

“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.”

Page 5: CTU June 2011 - C# 5.0 - ASYNC & Await

DemoConverting from synchronous,

to asynchronous, to using await

Page 6: CTU June 2011 - C# 5.0 - ASYNC & Await

async Task<string> LoadMoviesAsync()

{ var web = new WebClient();

var dTask = web.DownTaskAsync("http://netflix.com");

var rss = await dTask ;

var movies = XElement.Parse(rss).<story>.<description>;

return movies;

}

async void SearchButtonClick()

{ var task = QueryMoviesAsync();

var m = await task;

textBox1.Text = movies;

}

UIthread IOCP

threadHow the demo actually worked

Page 7: CTU June 2011 - C# 5.0 - ASYNC & Await

async void SearchButtonClick()

{ var task = QueryMoviesAsync();

var m = await task;

textBox1.Text = movies;

}

async Task<string> LoadMoviesAsync()

{ var web = new WebClient();

var dTask = web.DownTaskAsync("http://netflix.com");

var rss = await dTask ;

var movies = XElement.Parse(rss).<story>.<description>;

return movies;

}

UIthread IOCP

thread

Click

[1/12] A button-click arrives on the UI queue

Page 8: CTU June 2011 - C# 5.0 - ASYNC & Await

async void SearchButtonClick()

{ var task = QueryMoviesAsync();

var m = await task;

textBox1.Text = movies;

}

async Task<string> LoadMoviesAsync()

{ var web = new WebClient();

var dTask = web.DownTaskAsync("http://netflix.com");

var rss = await dTask ;

var movies = XElement.Parse(rss).<story>.<description>;

return movies;

}

UIthread IOCP

thread

Click

dTask

[2/12] Invoke some functions; get back “dTask” from the API

Page 9: CTU June 2011 - C# 5.0 - ASYNC & Await

async Task<string> LoadMoviesAsync()

{ var web = new WebClient();

var task = web.DownTaskAsync("http://netflix.com");

var rss = await task;

var movies = XElement.Parse(rss).<story>.<description>;

return movies;

}

dTask

async void SearchButtonClick()

{ var task = QueryMoviesAsync();

var m = await task;

textBox1.Text = movies;

}

UIthread IOCP

thread

Click

dTask » ui.Post{Κ1}

Κ1:

[3/12] “await task” assigns a continuation and returns task

task

Page 10: CTU June 2011 - C# 5.0 - ASYNC & Await

async void SearchButtonClick()

{ var task = QueryMoviesAsync();

var m = await task;

textBox1.Text = movies;

}

tasktask » ui.Post{Κ2}

async Task<string> LoadMoviesAsync()

{ var web = new WebClient();

var dTask = web.DownTaskAsync("http://netflix.com");

var rss = await dTask ;

var movies = XElement.Parse(rss).<story>.<description>;

return movies;

}

UIthread IOCP

thread

Click

dTask » ui.Post{Κ1}

Κ1:

Κ2:

[4/12] “await task” assigns a continuation and returns

Page 11: CTU June 2011 - C# 5.0 - ASYNC & Await

async void SearchButtonClick()

{ var task = QueryMoviesAsync();

var m = await task;

textBox1.Text = movies;

}

async Task<string> LoadMoviesAsync()

{ var web = new WebClient();

var dTask = web.DownTaskAsync("http://netflix.com");

var rss = await dTask ;

var movies = XElement.Parse(rss).<story>.<description>;

return movies;

}

UIthread IOCP

thread

Click

rss

dTask » ui.Post{Κ1}

Κ1:

Κ2:

task » ui.Post{Κ2}

[5/12] Network packet arrives with data

Page 12: CTU June 2011 - C# 5.0 - ASYNC & Await

async void SearchButtonClick()

{ var task = QueryMoviesAsync();

var m = await task;

textBox1.Text = movies;

}

async Task<string> LoadMoviesAsync()

{ var web = new WebClient();

var dTask = web.DownTaskAsync("http://netflix.com");

var rss = await dTask ;

var movies = XElement.Parse(rss).<story>.<description>;

return movies;

}

UIthread IOCP

thread

Click

rssui.Post{Κ1(rss)}

dTask » ui.Post{Κ1}

Κ1:

Κ2:

task » ui.Post{Κ2}

[6/12] Invoke dTask’s continuation with that data

Page 13: CTU June 2011 - C# 5.0 - ASYNC & Await

async void SearchButtonClick()

{ var task = QueryMoviesAsync();

var m = await task;

textBox1.Text = movies;

}

async Task<string> LoadMoviesAsync()

{ var web = new WebClient();

var dTask = web.DownTaskAsync("http://netflix.com");

var rss = await dTask ;

var movies = XElement.Parse(rss).<story>.<description>;

return movies;

}

UIthread IOCP

thread

Click

task » ui.Post{Κ2}

rss

K1(rss) Κ1:

Κ2:

ui.Post{Κ1(rss)}

[7/12] Continuation is a “Post”, i.e. addition to the UI queue

Page 14: CTU June 2011 - C# 5.0 - ASYNC & Await

async void SearchButtonClick()

{ var task = QueryMoviesAsync();

var m = await task;

textBox1.Text = movies;

}

async Task<string> LoadMoviesAsync()

{ var web = new WebClient();

var dTask = web.DownTaskAsync("http://netflix.com");

var rss = await dTask ;

var movies = XElement.Parse(rss).<story>.<description>;

return movies;

}

UIthread IOCP

thread

Click

rss

K1(rss) Κ1:

Κ2:

ui.Post{Κ1(rss)}

task » ui.Post{Κ2}

[8/12] UI thread executes K1, giving a result to the “await”

Page 15: CTU June 2011 - C# 5.0 - ASYNC & Await

async void SearchButtonClick()

{ var task = QueryMoviesAsync();

var m = await task;

textBox1.Text = movies;

}

async Task<string> LoadMoviesAsync()

{ var web = new WebClient();

var dTask = web.DownTaskAsync("http://netflix.com");

var rss = await dTask ;

var movies = XElement.Parse(rss).<story>.<description>;

return movies;

}

UIthread IOCP

thread

Click

rss

K1(rss) Κ1:

Κ2:

ui.Post{Κ1(rss)}

task » ui.Post{Κ2}

[9/12] “return movies” will signal completion of task

Page 16: CTU June 2011 - C# 5.0 - ASYNC & Await

async void SearchButtonClick()

{ var task = QueryMoviesAsync();

var m = await task;

textBox1.Text = movies;

}

async Task<string> LoadMoviesAsync()

{ var web = new WebClient();

var dTask = web.DownTaskAsync("http://netflix.com");

var rss = await dTask ;

var movies = XElement.Parse(rss).<story>.<description>;

return movies;

}

UIthread IOCP

thread

Click

rss

K1(rss)

ui.Post(Κ2(movie))

K2(movie)

Κ1:

Κ2:

ui.Post{Κ1(rss)}

task » ui.Post{Κ2}

[10/12] Invoke task’s continuation with data (by posting to UI queue)

Page 17: CTU June 2011 - C# 5.0 - ASYNC & Await

async void SearchButtonClick()

{ var task = QueryMoviesAsync();

var m = await task;

textBox1.Text = movies;

}

async Task<string> LoadMoviesAsync()

{ var web = new WebClient();

var dTask = web.DownTaskAsync("http://netflix.com");

var rss = await dTask ;

var movies = XElement.Parse(rss).<story>.<description>;

return movies;

}

UIthread IOCP

thread

Click

rss

K1(rss)

ui.Post(Κ2(movie))

K2(movie)

Κ1:

Κ2:

ui.Post{Κ1(rss)}

[11/12] Return from handling the K1 continuation

Page 18: CTU June 2011 - C# 5.0 - ASYNC & Await

async void SearchButtonClick()

{ var task = QueryMoviesAsync();

var m = await task;

textBox1.Text = movies;

}

async Task<string> LoadMoviesAsync()

{ var web = new WebClient();

var dTask = web.DownTaskAsync("http://netflix.com");

var rss = await dTask ;

var movies = XElement.Parse(rss).<story>.<description>;

return movies;

}

rss

K2(movie)

Click

K1(rss)

UIthread IOCP

thread

ui.Post(Κ2(movie))

Κ1:

Κ2:

ui.Post{Κ1(rss)}

[12/12] UI thread executes K2, giving a result to the “await”

Page 19: CTU June 2011 - C# 5.0 - ASYNC & Await

DemoUsing await and async with Silverlight

Page 20: CTU June 2011 - C# 5.0 - ASYNC & Await

// networkstring s = await webClient.DownloadStringTaskAsync("http://a.com");string s = await webClient.UploadStringTaskAsync(new Uri("http://b"), "dat");await WebRequest.Create("http://a.com").GetResponseAsync();await socket.ConnectAsync("a.com",80);await workflowApplication.RunAsync();await workflowApplication.PersistAsync();PingReply r = await ping.SendTaskAsync("a.com");

// streamstring s = await textReader.ReadToEndAsync();await stream.WriteAsync(buffer, 0, 1024);await stream.CopyToAsync(stream2);

// UIawait pictureBox.LoadTaskAsync("http://a.com/pic.jpg");await soundPlayer.LoadTaskAsync();

// task/await, assuming “task” of type IEnumerable<Task<T>>T[] results = await TaskEx.WhenAll(tasks);Task<T> winner = await TaskEx.WhenAny(tasks);Task<T> task = TaskEx.Run(delegate {... return x;});await TaskEx.Delay(100);await TaskEx.Yield();await TaskScheduler.SwitchTo();await Dispatcher.SwitchTo();

Ultimately the contents of TaskEx will be moved into Task.

How to use the “Task Async Pattern” [TAP]

Page 21: CTU June 2011 - C# 5.0 - ASYNC & Await

class Form1 : Form{ private void btnGo_Click(object sender, EventArgs e) { cts = new CancellationTokenSource(); cts.CancelAfter(5000); try { await new WebClient().DownloadStringTaskAsync(new Uri("http://a.com"), cts.Token); await new WebClient().DownloadStringTaskAsync(new Uri("http://b.com"), cts.Token); } catch (OperationCancelledException) { ... } finally {cts = null;} }

CancellationTokenSource cts;

private void btnCancel_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

Page 22: CTU June 2011 - C# 5.0 - ASYNC & Await

class Form1 : Form{ private void btnGo_Click(object sender, EventArgs e) { var cts = new CancellationTokenSource(); cts.CancelAfter(5000); btnCancel.Click += cts.EventHandler; try { // a slicker, more local way to handle cancellation... await new WebClient().DownloadStringTaskAsync(new Uri("http://a.com"), cts.Token); await new WebClient().DownloadStringTaskAsync(new Uri("http://b.com"), cts.Token); } catch (OperationCancelledException) { ... } finally {btnCancel.Click -= cts.EventHandler;} }}

public static class Extensions{ public static void EventHandler(this CancellationTokenSource cts, 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]

Page 23: CTU June 2011 - C# 5.0 - ASYNC & Await

private void btnGo_Click(object sender, EventArgs e){ var progress = new EventProgress<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 await new WebClient().DownloadStringTaskAsync(uri, cts.Token, progress);}

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)

interface IProgress<T>{ void Report(T value);}

How to use TAP progress

Page 24: CTU June 2011 - C# 5.0 - ASYNC & Await

// handle progress with a "while" loop, instead of a callback:var progress = new LatestProgress<DownloadProgressChangedEventArgs>();var task = new WebClient().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:var progress = new QueuedProgress<DownloadProgressChangedEventArgs>();var task = new WebClient().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]

Page 25: CTU June 2011 - C# 5.0 - ASYNC & Await

Task<string[]> GetAllAsync(Uri[] uris, CancellationToken cancel, IProgress<int> progress){ var results = new string[uris.Length]; for (int i=0; i<uris.Length; i++) { cancel.ThrowIfCancellationRequested(); results[i] = await new WebClient().DownloadStringTaskAsync(uris[i], cancel); if (progress!=null) progress.Report(i); } return results;}

1. 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.

2. 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...

3. Pass cancellation down: usually it will be appropriate to pass the cancellation down to nested async functions that you call.

4. 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

Page 26: CTU June 2011 - C# 5.0 - ASYNC & Await

Task Delay(int ms, CancellationToken cancel);

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 queryint[] results = await TaskEx.WhenAll(from url in urls select GetIntAsync(url));

// WhenAny to implement a concurrent worker poolQueue<string> todo = ...;var workers = new HashSet<Task<int>>();for (int i=0; i<10; i++) workers.Add(GetIntAsync(todo.Dequeue());while (workers.Count>0){ var winner = await TaskEx.WhenAny(workers); Console.WriteLine(await winner); workers.Remove(winner); if (todo.Count>0) workers.Add(GetIntAsync(todo.Dequeue());}

Task<T> combinators

Page 27: CTU June 2011 - C# 5.0 - ASYNC & Await

async void FireAndForgetAsync() { await t;}

async Task MerelySignalCompletionAsync() { return;}

Async Task<int> GiveResultAsync() { return 15;}

FireAndForgetAsync();

await MerelySignalCompletionAsync();

var r = await GiveResultAsync();

1. 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.

2. Task-returning asyncs: Used if you merely want to know when the task has finished. Exceptions get squirrelled away inside the resultant Task.

3. Task(Of T)-returning asyncs: Used if you want to know the result as well.

Three kinds of async method

Page 28: CTU June 2011 - C# 5.0 - ASYNC & Await

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

// Asynchronous Programming Model [APM]IAsyncResult BeginGetString(Params..., AsyncCallback Callback, object state);TR EndGetString(IAsyncResult);

// Event-based Asynchronous Pattern [EAP]class C{ public void GetStringAsync(Params...); public event GetStringCompletedEventHandler GetStringCompleted; public void CancelAsync();}

class GetStringCompletedEventArgs{ public TR Result { get; } public Exception Error { get; }}

Comparing TAP to its predecessors

Page 29: CTU June 2011 - C# 5.0 - ASYNC & Await

DemoProgress and Cancellation

Page 30: CTU June 2011 - C# 5.0 - ASYNC & Await

DemoAsync on Windows Phone 7

(if there’s time)

Page 31: CTU June 2011 - C# 5.0 - ASYNC & Await

Q & A

[email protected]

Page 32: CTU June 2011 - C# 5.0 - ASYNC & Await