Using Async in your Mobile Apps - Marek Safar

Post on 20-Jan-2015

1,464 views 0 download

Tags:

description

 

transcript

Marek SafarEngineerXamarinmsafar@xamarin.com

Using Async in Your Apps

You?

Wikipedia

“Asynchronous events are those occurring independently of the main program flow. Asynchronous actions are actions executed in a non-blocking scheme, allowing the main program flow to continue processing”

You Should Use Async Today

• Your UI always remains responsive• No need to guard against race conditions• No ugly callbacks• Compiler does all the heavy li!ing

Async in C#

• New major feature of C# 5.0Makes asynchronous programming a first-class citizen in C#Important part of C# – as significant as LINQ

• New async modifier keyword introduced• Methods, lambda expressions, and anonymous methods can be

asynchronous• New contextual keyword await introduced

Adding Async to Your Codebase

• Decorated with new async modifier• Async method must return one of

Task, Task<T> or void type

• Async anonymous method and lambdasDelegates returning Task, Task<T> or void

• Very few restrictionsNo ref or out parameters allowedNo unsafe codeNo Main async methodNo async iterators (aka IAsyncEnumerable<T>)

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

Example: Calculating a Price Asynchronously public  async  Task<int>  CalculatePriceAsync  (Item[]  items){int  price;//  Calculate  price  asynchronously  and  return  the  valuereturn  price;

}

public  override  void  ViewDidLoad  (){button.TouchDown  +=  async  (sender,  e)  =>  {

//  This  is  asynchronous  handler};

}

Await Introduction

• Await can be used in async context onlyMarks a suspension pointCan be used anywhere (except catch and finally blocks)Requires awaitable types (Task, Task<T> but can be any custom type)As many as you like inside async block

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

Calculating a Price Asynchronously public  async  Task<int>  CalculatePriceAsync  (Item[]  items){using  (var  cmd  =  CreateSqlCommand  (Calculations.TotalPrice,  items))  {

using  (var  reader  =  await  cmd.ExecuteReaderAsync  ())  {int  price  =  ReadPrice  (reader);return  price;

}}

}public  override  void  ViewDidLoad  (){button.TouchDown  +=  async  (sender,  e)  =>  {

var  price  =  await  CalculatePriceAsync  (items);priceLabel.Text  =  price.ToString  ();

};}

Cancelling an Async Task

• CancellationToken controls cancellation process• Async method needs explicitly support it

Usually another method overloadTask SaveAsync (CancellationToken token)

• Caller calls Cancel on CancellationTokenSourceAsync method stops and returns Task with cancelled stateImplementation detail how quickly async method terminates

DEMO

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

Async Language Featurespublic  async  Task  RealAsync  (){//  This  is  real  async  heavy  code  (only  7  awaits  someone  else  will//  certainly  do  better)var  test  =  new  TestClass  (await  CallAsync  ()  |  await  CallAsync  ())  {   Latitude  =  await  GetLatitudeAsync  (await  AnotherAsync  ()),   Roles  =  from  role  in  await  GetRolesAsync  ()

 where  role  ==  "agent"  select  role};....test  [await  CallAsync  ()]  =  new  int[]  {  33,  await  GetIntAsync  ()  };...

}

Best Practices

• Use Async naming suffix for asynchronous methodsLoadAsync, SendAsync, etc.Only recommended naming convention

• Return Task or Task<T> preferablyTask for SaveAsync like methodsTask<T> for ReadAsync like methodsLeave async void to event handlers only

• Support cancellation, if possible

Synchronization Context

• Framework abstraction over UI toolkit threading modelNSRunLoop, DispatchContext in Xamarin.iOSMainLooper in Xamarin.AndroidDispatcher in WPFetc.

• Await uses synchronization context to restore back suspended context

SynchronizationContext::Post method is usedSuspension on UI thread resumes back on same UI thread as “expected”

Sometimes Things Go Wrong

• Async returning Task or Task<T>Task state is set to FaultedException is re-thrown when Task is checked (awaited)

• Async voidFire and forget asynchronyException is thrown on current synchronization context and your app will crash

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

Exception Handling with Awaitpublic  override  void  ViewDidLoad  (){button.TouchDown  +=  async  (sender,  e)  =>  {

try  {button.Enabled  =  false;var  price  =  await  CalculatePriceAsync  (items);priceLabel.Text  =  price.ToString  ();

}  catch  (Exception  ex)  {//  Handles  price  calculation  errorpriceLabel.Text  =  "Calculation  failed";Debug.Print  (ex.ToString  ());  //  Simple  exception  logging

}  finally  {button.Enabled  =  true;

}};

}

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

Exception Throwingpublic  async  Task<int>  CalculatePriceAsync  (Item[]  items){if  (items  ==  null)  {

//  Throw  before  suspensionthrow  new  ArgumentNullException  ("items");

}...var  price  =  await  CalculatePriceAsync  (items);if  (price  <  0)  {

//  Throw  after  suspensionthrow  new  ArgumentException  ("Invalid  items");

}...

}

Diving Deeper

• Await can work with any type which implements the awaiter pattern

• Task and Task<T> types do since .NET 4.5• Any custom type can be made awaitable

Has an accessible method called GetAwaiter returning a type whichImplements the interface INotifyCompletionHas property IsCompleted of type boolHas method GetResult with no parameters

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

Custom await Behaviourpublic  async  Task<int>  SomeAsync  (){    ...    await  3000;  //  This  does  not  compile  unless  we  provide  custom  awaiter    ...}

//  Name  of  the  method  is  importantstatic  TaskAwaiter  GetAwaiter  (this  int  millisecondsDelay){    return  Task.Delay  (millisecondsDelay).GetAwaiter  ();}

Async Deadlocks

• await restores execution context based on current SynchronizationContext

Good for most application codeBad for most library code

• Don’t block the UI threadGood old rule still applies in async worldUse await when possible

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

Async Deadlock Examplepublic  async  Task<int>  CalculatePriceAsync  (Item[]  items){using  (var  cmd  =  CreateSqlCommand  (Calculations.TotalPrice,  items))  {

using  (var  r  =  await  cmd.ExecuteReaderAsync  ())  {.....

}}

}

//  The  method  itself  cannot  be  asyncpublic  override  bool  FinishedLaunching  (UIApplication  app,  NSDictionary  options){....int  price  =  CalculatePriceAsync  (items).Result;    //  Synchronous  wait//  This  line  won’t  be  reached  if  flow  suspension  occurred

}

Controlling the Synchronization Context

• ConfigureAwait (bool)Controls captured context behaviour

• true value - default behaviourContinue execution on context async was called fromImportant for UI to switch back to UI context

• false valueAvoids expensive context switchingAvoids possible deadlocks on blocking UI

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

Async Deadlock Fixedpublic  async  Task<int>  CalculatePriceAsync  (Item[]  items){using  (var  cmd  =  CreateSqlCommand  (Calculations.TotalPrice,  items))  {using  (var  r  =  await  cmd.ExecuteReaderAsync  ().ConfigureAwait  (false))  {//  The  context  is  not  restored  but  that’s  fine  for  no  UI  method  

}}

}

//  The  method  itself  cannot  be  asyncpublic  override  bool  FinishedLaunching  (UIApplication  app,  NSDictionary  options){....int  price  =  CalculatePriceAsync  (items).Result;    //  Synchronous  wait//  Program  continues  when  the  wait  is  signalled

}

Combinators

• Asynchronously wait on multiple asynchronous operationsBetter than individual awaits of multiple tasksThe result can be awaited (Task of Tasks)

• Task.WhenAll (IEnumerable<Task>)Completes when all of the individual tasks have completed

• Task.WhenAny (IEnumerable<Task>)Completes when any of the individual tasks have completed

The Compiler Magic

• Compiler does all the “magic”• Local variable and parameters are li!ed into compiler generated

typeCompiler converts variables and parameters to fields

• Any stack values need to be li!ed tooStack needs to be restored a"er suspension

• Try-Catch block over any async block• No suspension when awaited task finished

Best Practices - Performance

• Use ConfigureAwait when you can• Reduce number of local variables

Move async block into separate method or lambda expressionPass variables as parametersAvoid deep await usage

• Cache Task not task result• Don’t over use async

Use async only when appropriate (unreliable tasks, running >50ms)Async has its own overhead

Get Your Hands on Async

• Async is available todayXamarin.iOS Beta releaseXamarin.Android Beta releaseXamarin Studio async support

• Give us feedback on our new async APIForumsBugzillaMailing listIRC

Q&A

THANK YOU