Async void is only for toplevel event handlers

















































- Slides: 49
Async void is only for top-level event handlers. Use threadpool for CPU-bound code, but not IO-bound. Use Task. Completion. Source to wrap Tasks around events. Libraries shouldn't lie, and should be chunky.
private async void Button 1_Click(object Sender, Event. Args e) { try { Send. Data("https: //secure. flickr. com/services/oauth/request_token"); await Task. Delay(2000); Debug. Print("Received Data: " + m_Get. Response); } catch (Exception ex) { root. Page. Notify. User("Error posting data to server. " + ex. Message); } } private async void Send. Data(string Url) { var request = Web. Request. Create(Url); using (var response = await request. Get. Response. Async()) using (var stream = new Stream. Reader(response. Get. Response. Stream())) m_Get. Response = stream. Read. To. End(); }
private async void Button 1_Click(object Sender, Event. Args e) { try { Send. Data("https: //secure. flickr. com/services/oauth/request_token"); // await Task. Delay(2000); // Debug. Print("Received Data: " + m_Get. Response); } catch (Exception ex) { root. Page. Notify. User("Error posting data to server. " + ex. Message); } } private async void Send. Data(string Url) { var request = Web. Request. Create(Url); using (var response = await request. Get. Response. Async()) // exception on resumption using (var stream = new Stream. Reader(response. Get. Response. Stream())) m_Get. Response = stream. Read. To. End(); }
private async void Button 1_Click(object Sender, Event. Args e) { try { Async await Send. Data("https: //secure. flickr. com/services/oauth/request_token"); await Task. Delay(2000); Debug. Print("Received Data: " + m_Get. Response); } catch (Exception ex) { root. Page. Notify. User("Error posting data to server. " + ex. Message); } } Task Async private async void Send. Data(string Url) { var request = Web. Request. Create(Url); using (var response = await request. Get. Response. Async()) using (var stream = new Stream. Reader(response. Get. Response. Stream())) m_Get. Response = stream. Read. To. End(); }
// Q. It sometimes shows Pixel. Width and Pixel. Height are both 0 ? ? ? Bitmap. Image m_bmp; protected override async void On. Navigated. To(Navigation. Event. Args e) { base. On. Navigated. To(e); await Play. Intro. Sound. Async(); class Layout. Aware. Page : Page image 1. Source = m_bmp; { Canvas. Set. Left(image 1, Window. Current. Bounds. Width private string _page. Key; - m_bmp. Pixel. Width); } protected override void On. Navigated. To(Navigation. Event. Args e) protected override async void Load. State(Object nav, Dictionary<String, Object> page. State) { { m_bmp = new Bitmap. Image(); if (this. _page. Key != null) return; var file = await Storage. File. Get. File. From. Application. Uri. Async("ms-appx: ///pic. png"); this. _page. Key = "Page-" + this. Frame. Back. Stack. Depth; using (var stream = await file. Open. Read. Async()) {. . . await m_bmp. Set. Source. Async(stream); this. Load. State(e. Parameter, null); } }
// A. Use a task Task<Bitmap. Image> m_bmp. Task; protected override async void On. Navigated. To(Navigation. Event. Args e) { base. On. Navigated. To(e); await Play. Intro. Sound. Async(); var bmp = await m_bmp. Task; image 1. Source = bmp; Canvas. Set. Left(image 1, Window. Current. Bounds. Width - bmp. Pixel. Width); } protected override void Load. State(Object nav, Dictionary<String, Object> page. State) { m_bmp. Task = Load. Bitmap. Async(); } private async Task<Bitmap. Image> Load. Bitmap. Async() { var bmp = new Bitmap. Image(); . . . return bmp; }
// In. VB, C#, theexpression context determines whether async is void- or (not Task-returning. ' In itself determines void-lambda or Task-returning the context). Action a 1 = async () =>Sub() { await Load. Async(); m_Result="done"; }; Dim void_returning = Async Func<Task> a 2 = async () => { await Load. Async(); m_Result="done"; }; Await Load. Async() : m_Result = "done" End Sub // Which one will pick? Dim. Q. task_returning = it Async Function() await Task. Run( async () => { await Load. Async(); m_Result="done"; }); Await Load. Async() : m_Result = "done" End Function // A. If both overloads are offered, it will pick Task-returning. Good! class Taskoverloads are offered, you must give it Task-returning. ' If both { Await Task. Run(Async Function(). . . End Function) static public Task Run(Action a) {. . . } static public Task Run(Func<Task> a) {. . . }
try { await Dispatcher. Run. Async(Core. Dispatcher. Priority. Normal, async () => { await Load. Async(); m_Result = "done"; throw new Exception(); }); } catch (Exception ex) { } finally { Debug. Print(m_Result); } // IAsync. Action Run. Async(Core. Dispatcher. Priority priority, Dispatched. Handler agile. Callback); // delegate void Dispatched. Handler();
request in // table 1. Data. Source = Load. Houses. Sequentially(1, 5); work 1 // table 1. Data. Bind(); public List<House> Load. Houses. Sequentially(int first, int last) { work 2 var loaded. Houses = new List<House>(); for (int i = first; i <= last; i++) { work 3 House house = House. Deserialize(i); loaded. Houses. Add(house); } work 4 return loaded. Houses; } t ou e s on s p s e r 500 m work 5
request in // table 1. Data. Source = Load. Houses. In. Parallel(1, 5); Parallel. For // table 1. Data. Bind(); public List<House> Load. Houses. In. Parallel(int first, int last) { work 1 work 2 var loaded. Houses = new Blocking. Collection<House>(); 4 1 2 Parallel. For(first, last+1, i => { work 3 House house = House. Deserialize(i); loaded. Houses. Add(house); }); 5 3 return loaded. Houses. To. List(); work 5 } response out 300 ms work 4
request in start 1 end 1 start 2 end 2 start 3 end 3 start 4 end 4 t ou e s on s p s e r 500 m start 5 end 5
request in sta 1 t 2 2 nd 2 d 1 start 2 end 1 end 2 start 5 4 sta rt 4 e en d 4 en star rt 1 Parallel. For star 5 end 5 t 5 start 3 3 end 5 end 3 response out ~200 ms start 3 end 3 start 4 end 4
request in start 1 start 2 start 3 start 4 start 5 end 2 end 5 end 1 end 3 end 4 response out ~100 ms
// table 1. Data. Source = await Load. Houses. Async(1, 5); // table 1. Data. Bind(); public async Task<List<House>> Load. Houses. Async(int first, int last) { var tasks = new List<Task<House>>(); for (int i = first; i <= last; i++) { Task<House> t = House. Load. From. Database. Async(i); tasks. Add(t); } House[] loaded. Houses = await Task. When. All(tasks); return loaded. Houses. To. List(); }
public async Task<List<House>> Load. Houses. Async(int first, int last) { var loaded. Houses = new List<House>(); var queue = new Queue<int>(Enumerable. Range(first, last – first + 1)); // Throttle the rate of issuing requests. . . var worker 1 = Worker. Async(queue, loaded. Houses); var worker 2 = Worker. Async(queue, loaded. Houses); var worker 3 = Worker. Async(queue, loaded. Houses); await Task. When. All(worker 1, worker 2, worker 3); return loaded. Houses; } private async Task Worker. Async(Queue<int> queue, List<House> results) { while (queue. Count > 0) { int i = queue. Dequeue(); var house = await House. Load. From. Database. Async(i); results. Add(house); } }
Protected Overrides Sub On. Pointer. Pressed(e As Pointer. Routed. Event. Args) Dim apple = CType(e. Original. Source, Image) Add. Handler apple. Pointer. Released, Sub(s, e 2) Dim endpt = e 2. Get. Current. Point(Nothing). Position If Not Basket. Catchment. Area. Bounds. Contains(endpt) Then Return Canvas. Set. ZIndex(apple, 1) ' mark apple as no longer free If Free. Apples. Count > 0 Then m_Action. After. Animation = Sub() Whoosh. Sound. Stop() Show. Victory. Window() Add. Handler btn. Ok. Click, Sub(). . End Sub End If Whoosh. Sound. Play() Animate. Then. Do. Action(Apple. Into. Basket. Storyboard)
Protected Async Sub On. Pointer. Pressed(e As Pointer. Routed. Event. Args) ' Let user drag the apple Dim apple = CType(e. Original. Source, Image) Dim endpt = Await Drag. Async(apple) If Not Basket. Catchment. Area. Bounds. Contains(endpt) Then Return ' Animate and sound for apple to whoosh into basket Dim animate. Task = Apple. Storyboard. Play. Async() Dim sound. Task = Whoosh. Sound. Play. Async() Await Task. When. All(animate. Task, sound. Task) If Free. Apples. Count = 0 Then Return ' Show victory screen, and wait for user to click button Await Start. Victory. Screen. Async() Await btn. Play. Again. When. Clicked() On. Start() End Sub
' Await storyboard 1. Play. Async(); storyboard 1. Play. Async() // Usage: await <Extension> Async Function Play. Async(sb As Animation. Storyboard) As Task public static async Task Play. Async(this Storyboard storyboard) { Dim Asnew New. Task. Completion. Source<object>(); Task. Completion. Source(Of Object) var tcs = Dim lambda As Event. Handler(Of Object) Sub() tcs. Try. Set. Result(Nothing) Event. Handler<object> lambda = (s, e) =>=tcs. Try. Set. Result(null); Try try { Add. Handler sb. Completed, lambda storyboard. Completed += lambda; sb. Begin() storyboard. Begin(); Await await tcs. Task; Finally } Remove. Handler sb. Completed, lambda finally { End storyboard. Completed Try -= lambda; End } Function }
// Usage: await button 1. When. Clicked(); public static async Task When. Clicked(this Button button) { var tcs = new Task. Completion. Source<object>(); Routed. Event. Handler lambda = (s, e) => tcs. Try. Set. Result(null); try { button. Click += lambda; await tcs. Task; } finally { button. Click -= lambda; } }
synchronous Foo(); perform var task = Foo. Async(); . . . await task; when it’s done asynchronous initiate immediately
Synchronous public static void Pause. Print() { var end = Date. Time. Now + Time. Span. From. Seconds(10); while (Date. Time. Now < end) { } Console. Write. Line("Hello"); } Asynchronous public static Task Pause. Print 2 Async() { return Task. Run(() => “Should I expose async wrappers for Pause. Print()); } synchronous methods? ” – generally no! // “I want to offer an async signature, http: //blogs. msdn. com/b/pfxteam/archive/2012/03/24/10287244. aspx // but my underlying library is synchronous” Task Pause. Print. Async() { public static async Task Pause. Print. Async() { public static void Pause. Print 2() { var tcs = new Task. Completion. Source<bool>(); await Task. Delay(10000); Task t = Pause. Print. Async(); new Timer(_ => { Console. Write. Line("Hello"); t. Wait(); Console. Write. Line("Hello"); “How can I expose sync wrappers for async } } tcs. Set. Result(true); // “I’m not– allowed async signature, methods? ” if you an absolutely have to, you }). Change(10000, Timeout. Infinite); // but my underlying library is async” can use a nested message-loop… return tcs. Task; http: //blogs. msdn. com/b/pfxteam/archive/2012/04/13/10293638. aspx }
threadpool is an In a server app, spinning up threads hurts scalability The app is in the best position to manage its threads synchronous asynchronous blocks the current thread without spawning new threads
Click Message pump Task. . . void Button 1_Click(){ Load. Async var t = Load. Async(); t. Wait(); Update. View(); } async Task Load. Async() { await IO. Network. Download. Async(path); } Task. . . Download. Async
server scalability.
We all know sync methods are “cheap” public static void Simple. Body() { Console. Write. Line("Hello, Async World!"); }. method public hidebysig static void Simple. Body() cil managed {. maxstack 8 L_0000: ldstr "Hello, Async World!" L_0005: call void [mscorlib]System. Console: : Write. Line(string) L_000 a: ret }
. method public hidebysig instance void Move. Next() cil managed { // Code size 66 (0 x 42). maxstack 2. locals init ([0] bool '<>t__do. Finally. Bodies', [1] class [mscorlib]System. Exception '<>t__ex'). try { IL_0000: ldc. i 4. 1 IL_0001: stloc. 0 IL_0002: ldarg. 0 IL_0003: ldfld int 32 Program/'<Simple. Body>d__0': : '<>1__state' IL_0008: ldc. i 4. m 1 public static async Task Simple. Body() { IL_0009: bne. un. s IL_000 d Console. Write. Line("Hello, Async World!"); IL_0041 IL_000 b: leave. s } IL_000 d: ldstr "Hello, Async World!" IL_0012: call void [mscorlib]System. Console: : Write. Line(string) IL_0017: leave. s IL_002 f. method public hidebysig static class [mscorlib]System. Threading. Tasks. Task Simple. Body() cil managed } { catch [mscorlib]System. Exception. custom instance void [mscorlib]System. Diagnostics. Debugger. Step. Through. Attribute: : . ctor() = ( 01 00 00 00 ) { // Code size 32 (0 x 20) IL_0019: stloc. 1. maxstack 2 IL_001 a: ldarg. 0. locals init ([0] valuetype Program/'<Simple. Body>d__0' V_0) IL_001 b: ldc. i 4. m 1 IL_0000: ldloca. s V_0 IL_001 c: stfld int 32 Program/'<Simple. Body>d__0': : '<>1__state' IL_0002: call valuetype [mscorlib]System. Runtime. Compiler. Services. Async. Task. Method. Builder IL_0021: ldarg. 0 [mscorlib]System. Runtime. Compiler. Services. Async. Task. Method. Builder: : Create() IL_0022: ldflda valuetype [mscorlib]System. Runtime. Compiler. Services. Async. Task. Method. Builder IL_0007: stfld valuetype [mscorlib]System. Runtime. Compiler. Services. Async. Task. Method. Builder Program/'<Simple. Body>d__0': : '<>t__builder' IL_000 c: ldloca. s V_0 IL_0027: ldloc. 1 IL_000 e: call instance void Program/'<Simple. Body>d__0': : Move. Next() IL_0028: call instance void [mscorlib]System. Runtime. Compiler. Services. Async. Task. Method. Builder: : Set. Exception( IL_0013: ldloca. s V_0 class [mscorlib]System. Exception) IL_0015: ldflda valuetype [mscorlib]System. Runtime. Compiler. Services. Async. Task. Method. Builder Program/'<Simple. Body>d__0': : '<>t__builder' IL_002 d: leave. s IL_0041 IL_001 a: call instance class [mscorlib]System. Threading. Tasks. Task [mscorlib]System. Runtime. Compiler. Services. Async. Task. Method. Builder: : get_Task() } IL_001 f: ret IL_002 f: ldarg. 0 } IL_0030: ldc. i 4. m 1 IL_0031: stfld int 32 Program/'<Simple. Body>d__0': : '<>1__state' IL_0036: ldarg. 0 IL_0037: ldflda valuetype [mscorlib]System. Runtime. Compiler. Services. Async. Task. Method. Builder Program/'<Simple. Body>d__0': : '<>t__builder' IL_003 c: call instance void [mscorlib]System. Runtime. Compiler. Services. Async. Task. Method. Builder: : Set. Result() IL_0041: ret } Not so for asynchronous methods
public static async Task<int> Get. Next. Int. Async() { if (m_Count == m_Buf. Length) { m_Buf = await Fetch. Next. Buffer. Async(); m_Count = 0; } m_Count += 1; return m_Buf[m_Count - 1]; }
var x = await Get. Next. Int. Async(); var $awaiter = Get. Next. Int. Async(). Get. Awaiter(); if (!$awaiter. Is. Completed) { DO THE AWAIT/RETURN AND RESUME; } var x = $awaiter. Get. Result();
public static async Task<int> Get. Next. Int. Async() { if (m_Count == m_Buf. Length) { m_Buf = await Fetch. Next. Buffer. Async(); m_Count = 0; } m_Count += 1; return m_Buf[m_Count - 1]; }
The heap is an app-global resource. Like all heap allocations, async allocations can contributing to hurting GC perf.
Sync context represents a “target for work” “Await task” uses the sync context “where you were before” But for library code, it’s rarely needed! You can use “await task. Configure. Await(false)” This suppresses step 2; instead if possible it resumes “on the thread that completed the task” Result: slightly better performance. Also can avoid deadlock if a badly-written user blocks.
UI responsiveness