Friday, April 3, 2015

Async Cancellation Tokens

ASP.NET has 3 built in Cancellation tokens that allow us to terminate an in-flight async task - say cancel even a database query that is executing. For instance, when the token is marked as Cancel, then ASP.NET terminates the async task to return the thread back to the threadpool.

The three built-in tokens are:

  • RequestTimeout - request timed out so discontinue
  • ClientDisconnected - client closed the browser, so discontinue async work
  • AsyncTimeout - set a timeout for my async task


Note: Always use RegisterAsyncTask when doing async code in ASP.NET. It is a best practice.

Example code snippet on Page_Load of ASP.NET Webforms,

RegisterAsyncTask(new PageAsyncTask(async ()=>
   {
      var token = Response.ClientDisconnectedToken;

       var stockJson = await new HttpClient().GetStringAsync(Constants.StockServiceUri, token);
       var stock = JsonConvert.DeserializeObject<Stock>(stockJson);
       DisplayStock(stock);

       var rssXml = await new HttpClient().GetStringAsync(Constants.RssUri, token);
       var rss = Desserialize(rssXml);
        DisplayRss(rss);
   }));

Note: The GetStringAsync is an extension method you can define as

public static class HttpClientExtensions
{
      public static async Task<string> GetStringAsync(
                 this HttpClient client,
                 string requestUri,
                 CancellationToken token) // this parameter is optional!!
     {
           var response = await client.GetAsync(requestUri, token);
           var result = await response.Content.ReadAsStringAsync();
           return result;
     }
}

**************************************************************************

The Webforms, WebAPI and MVC APIs allow only passing in one token object at a time, so we can wire all three tokens in a single object and pass it in! We can build a composite token this way,

var source = CancellationTokenSource.CreateLinkedTokenSource(
      Response.ClientDisconnectedToken,
      Request.TimedOutToken,
      t); //where t is the AsyncTimeOut token

//Use this composite source object
var token = source.Token;

Make sure to pass in "t" as a parameter to the lambda expression for PageAsyncTask like so

new PageAsyncTask(async (t) =>

*************************************************************************

WebApi and MVC actually makes this simpler by simply taking in a CancellationToken parameter in its method signatures. Also retrospectively adding this parameter does not affect any existing clients that don't pass in this token.

Example WebApi Get,

private StockContext entityDb = new StockContext();

public async Task<Stock> Get(CancellationToken token)
{
     return await entityDb.Stocks.FirstAsync(token);
}

Takeaways:

  1. Cancellation tokens further reduce thread usage and supplement async/await
  2. ASP.NET has built-in Cancellation tokens
  3. Use CancellationTokenSource to combine tokens


No comments:

Post a Comment