Talk: Async best practices


I’ve talked about async best practices a lot, and each time I’ve tried to refine the talk a little further. Here’s the best I can explain how to use async effectively!

1. For goodness’ sake, stop using async void. Async void is for top-level event-handlers, and event-handler like things. I see too many people using it elsewhere in their code where they shouldn’t. Please stop.

2. Use async for IO-bound tasks, and threadpool for CPU-bound tasks. Use async only for disk and IO bound stuff. Use the threadpool (Task.Run, Parallel.For, LINQ AsParallel) only for CPU-bound stuff. If you get this wrong, then your server code won’t scale.

3. Use TaskCompletionSource to wrap events up into Tasks. Event-based programming with callbacks and lambdas are hard, and you must learn how to use TaskCompletionSource to tame them.

4. Libraries shouldn’t lie. The threadpool is an app-global resource, so you should leave the callers of your library to decide how and when they want to use it. Your library methods should not use the threadpool (Task.Run, Parallel.For, …) internally in secret. They should have async signatures if and only if their internal implementations are truly async.

5. Libraries should have chunky async APIs. But if you can’t, there’s a micro-optimization you can use for async methods based around the fast-path which can reduce or eliminate the heap overhead of async methods. Only consider this micro-optimization after profiling your app, of course.

6. Libraries should consider Task.ConfigureAwait(false). There’s another micro-optimization you can use to reduce your methods’ impact on the UI thread. Consider this if your library routine might be called from the UI thread, and has chatty awaits inside it. This technique is used throughout the .NET Framework because it also avoids deadlock in certain poor-practice user code.

 

 


 

Earlier version. At the 2013 MVP summit R2 in Redmond, I gave a talk on “Async Best Practices”. This talk was largely a condensed version of two earlier talks, [here], which also have video-recordings. But in case anyone wants the slides from the MVP-summit-version, here they are:

 

We also held three hours of “Async Clinic”, where people could come with their questions. Other experts in the room were Stephen Cleary MVP, and Stephen Toub, Mads Torgersen and Alex Turner. I took notes as we went. These notes are in very rough form. I hope to revise them into something more useful.

The last piece of code “pod7.vb” relates to a question that was asked. Someone wanted to write an app which did a lot of web-scraping, then processing the results. To architect this, I recommend keeping a queue of workitems for each step along the pipeline. Then you can have a worker, either a synchronous method on a thread or just an asynchronous method, whose job is to retrieve items from the queue and process them and dump them into the next queue. This way you can have diagnostics that show what’s doing what. Anyway, I wrote this code to scrape a website (IO-bound), stream down audio (IO-bound), expand it to PCM (disk-bound), compress it to MP3 (cpu-bound). It’s not best-practice code, and it doesn’t demonstrate Async at all, but it does demonstrate queues and workers and scraping.

 

Comments (10)

  1. Durval Ramos says:

    Great ! Thanks for sharing

  2. hanif says:

    Great Bro Thanks for sharing thanks very much

  3. Todd Miranda says:

    I enjoyed the presentation.  I picked up a few things that I will add to my toolbox!  Thanks very much for the time in putting it together.

  4. Peter Kellner says:

    Great set of talks! as my nephew said at dinner the other night when I shot a pea sprout across the room, "Highlight!".

    does your paragraph on screen scraping above meant to answer the question regarding how to limit the number of IO bound async work items from running at once?  That is, my problem is I want to send millions of emails, but only have 200 at once attempting to go out.  I don't want to start millions of async emails to go out and then flood all my io channels. Do you have any sample code for a scenario like that?  If you have no idea what I'm talking about, say so and I'll explain better.

  5. @Peter, in the async clinic "slides.pptx" there are three slides on the topic of Throttling. These show different possible techniques that all directly apply to your question — i.e. how to have only 200 async work items running at a time…

    Throttling(1) – this is a general purpose technique. You'd post all your workitems (i.e. the email recipients) into the throttle. The technique ensures that the async lambda you write is run at most 200 times. However, I don't know if the throttle will scale up to millions of items in the queue.

    Throttling(2) – this technique as written on the slide makes use of a Queue<T> and achieves threadsafety by the assumption that it's being run on the UI thread. But you have no need for a single-threaded context, and so you'd have to replace it with a ConcurrentQueue or similar.

    Throttling(3) – this slide just outlines some other available primitives you can build throttling out of.

    Not on any slides – to be frank, if you're running this bulk-emailer on a dedicated machine, and you wish to limit yourself to 200 at a time, then you could equally well use 200 threads executing non-async code, since that "200" number is an upper limit on how much scalability you need. (On the other hand, if you're running it on a VM and wish to minimize how many VM CPU cycles you use, it's unknown whether async or threads will result in less resource utilization for your task).

    That said, I've only brushed with bulk-emailing briefly, but I know it's a difficult topic, and throttling the rate of async calls will only be the smallest of your worries. You'll also have to manage failures, and timeouts, and what if your bulk-emailing machine crashes when it's half way through sending everything and needs to resume where it left off upon restore? Good luck!

  6. Steve O says:

    Hi Lucian,

    Thanks so much for posting the pod7.vb file.  I learned a few tricks from seeing this.  It's great to see how a master does it, and there are unfortunately fewer and fewer truly excellent examples of VB.NET code being published on the Internet today.  Many thanks for all you've posted over the years; it's some of the highest quality content out there.

    -Steve O

  7. Bayer White says:

    Hey Lucian,

    I know this is late however I wanted to comment on how impressed I was with your talk at the summit, and the outline for the material you presented for Async. This is a topic I find myself going back to again and again to learn more and solidify concepts.

  8. Dick Baker says:

    Lucien, thanks for useful tips. I suggest that in the TipsforAsyncOverEvents_Source.wmv you have code

    If FreeApples.Count = 0 Then Return

    whereas I think the condition is inverted and should read

    If FreeApples.Count != 0 Then Return

    so that control can fall into the following code to show victory screen etc

  9. Luciano says:

    Hi Lucian. I like your series very much. I have one question: I have a synchronous method that executes some database code. I want to make an  asynchronous version of it. My first thought was to wrap the method in a Task.Run but that will consume a thread from the threadpool. I know the database operation will be mostly IO. Is there any other alternative?. So the general question will be how can  I make a sync method async without the threadpool? or is it even posible?

    Bye, Luciano.

  10. @Luciano, no I'm afraid it's not possible. To get async scalability benefits you have to be async all the way down. Stephen Toub wrote a great blog post dedicated solely to this question:

    blogs.msdn.com/…/10287244.aspx