Talk: The New Async Design Patterns


Talk: The New Async Design Patterns

Async involves some new concepts. They’re not difficult; just unfamiliar. Over the past year I’ve been watching how people use it. This talk distils out the three top async patterns and anti-patterns.

  1. Async void is for top-level event-handlers only, and event-like things. Don’t use it elsewhere in your code.
  2. You can wrap events up in Task-returning APIs and await them. This can dramatically simplify code.
  3. It’s crucial to distinguish CPU-bound work (should be done on threadpool) from IO-bound work (which needn’t).
If you understand the basic flow of control in an async method, then those three points all fall naturally into place.
 
 
 
 
 
 
 
PS. The first anti-pattern is “async void”. People of course ask: “If it’s so wrong, then why did you allow it?” That’s a good question. I’ll write a blog post solely on this question next week.
 
Comments (10)

  1. Simon says:

    Surely if you treat it like you should any new threaded operation and not allow exceptions to bubble up for example you'd be okay?

    Though why you'd want to await something that doesn't return anything seems silly – like a bad API design on the part of the person defining the void operation (presumably it's changing some internal state it shouldn't touch and simultaneously doing too much work in one place).

    Really the compiler should only allow calling async void's with an operator for fire and forget that just starts the method on a new thread, though the overhead to do that manually is quite low so I'm not sure the need was there, it is now you added 'await' simply for parity of implementation.

  2. @Simon, I agree, it's bad API design to create an async void, since no one can await it. And it's also true that if you create an async void method, then it shouldn't leak exceptions.

    **BUT**

    The fire-and-forget would not have anything to do with threads. Remember that the async keyword by itself doesn't make things run on new threads. The ONLY way to run thing on a new thread is if you explicitly invoke an API to do that, e.g. Task.Run.

    So the fire-and-forget stuff would instead look like this:

    FooAsync().FireAndForget()

    Module ExtensionMethods

       <System.Runtime.CompilerServices.Extension>

       Async Sub FireAndForget(this As Task)

           Try

               Await this

           Catch ex As Exception

               ' here is a great opportunity to log the exception, or do something else with it

           End Try

       End Sub

    End Module

  3. Thomas says:

    Hi,

    Anyone know if the video has been uploaded?

  4. I've uploaded the videos now.

  5. Richard LH says:

    Please make that

    private async void Button1_Click(object sender, Eventargs e)

    {

       Button1.Enabled(false);

       Button1_Enabled(true);

    }

    otherwise simple re-entrancy is possible through the user clicking more than once. I suspect that this sort of thing will be a common problem in the future.

  6. Good point, @Richard. I included a slide about re-entrancy and disabling buttons in the slide deck. (didn't have time to fit it into the talk).

  7. Great article by C# MVP Filip Ekberg on "Avoid shooting yourself in the foot with tasks and async" – covers similar issues about async void.

    blog.filipekberg.se/…/avoid-shooting-yourself-in-the-foot-with-tasks-and-async

  8. swetha k says:

    Excellent Post. Also Visit http://www.msnetframework.com

  9. Sam says:

    When I see an async lambda whose handler does not take a Func<Task> and I cannot modify the handler. What should I do instead to stay unblocking?

    Maintain a concurrent dictionary where I add those tasks to and append a continuation task to each that removes the parent task from dictionary?