Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is async/await suitable for methods that are both IO and CPU bound?

The MSDN documentation appears to state that async and await are suitable for IO-bound tasks whereas Task.Run should be used for CPU-bound tasks.

I'm working on an application that performs HTTP requests to retrieve HTML documents, which it then parses. I have a method that looks like this:

public async Task<HtmlDocument> LoadPage(Uri address) {     using (var httpResponse = await new HttpClient().GetAsync(address)) //IO-bound     using (var responseContent = httpResponse.Content)     using (var contentStream = await responseContent.ReadAsStreamAsync())         return await Task.Run(() => LoadHtmlDocument(contentStream)); //CPU-bound } 

Is this good and suitable use of async and await, or am I over-using it?

like image 896
Sam Avatar asked Feb 15 '13 14:02

Sam


2 Answers

There are two good answers already, but to add my 0.02...

If you're talking about consuming asynchronous operations, async/await works excellently for both I/O-bound and CPU-bound.

I think the MSDN docs do have a slight slant towards producing asynchronous operations, in which case you do want to use TaskCompletionSource (or similar) for I/O-bound and Task.Run (or similar) for CPU-bound. Once you've created the initial Task wrapper, it's best consumed by async and await.

For your particular example, it really comes down to how much time LoadHtmlDocument will take. If you remove the Task.Run, you will execute it within the same context that calls LoadPage (possibly on a UI thread). The Windows 8 guidelines specify that any operation taking more than 50ms should be made async... keeping in mind that 50ms on your developer machine may be longer on a client's machine...

So if you can guarantee that LoadHtmlDocument will run for less than 50ms, you can just execute it directly:

public async Task<HtmlDocument> LoadPage(Uri address) {   using (var httpResponse = await new HttpClient().GetAsync(address)) //IO-bound   using (var responseContent = httpResponse.Content)   using (var contentStream = await responseContent.ReadAsStreamAsync()) //IO-bound     return LoadHtmlDocument(contentStream); //CPU-bound } 

However, I would recommend ConfigureAwait as @svick mentioned:

public async Task<HtmlDocument> LoadPage(Uri address) {   using (var httpResponse = await new HttpClient().GetAsync(address)       .ConfigureAwait(continueOnCapturedContext: false)) //IO-bound   using (var responseContent = httpResponse.Content)   using (var contentStream = await responseContent.ReadAsStreamAsync()       .ConfigureAwait(continueOnCapturedContext: false)) //IO-bound     return LoadHtmlDocument(contentStream); //CPU-bound } 

With ConfigureAwait, if the HTTP request doesn't complete immediately (synchronously), then this will (in this case) cause LoadHtmlDocument to be executed on a thread pool thread without an explicit call to Task.Run.

If you're interested in async performance at this level, you should check out Stephen Toub's video and MSDN article on the subject. He has tons of useful information.

like image 178
Stephen Cleary Avatar answered Sep 23 '22 13:09

Stephen Cleary


It is appropriate to await any operation that is asynchronous (i.e. is represented by a Task).

The key point is that for IO operations, whenever possible, you want to use a provided method that is, at it's very core, asynchronous, rather than using Task.Run on a blocking synchronous method. If you're blocking a thread (even a thread pool thread) while performing IO, you're not leveraging the real power of the await model.

Once you have created a Task that represents your operation you no longer care if it's CPU or IO bound. To the caller it's just some async operation that needs to be await-ed.

like image 24
Servy Avatar answered Sep 20 '22 13:09

Servy