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

The MSDN documentation states that async and await are suitable for IO-bound tasks, while Task.Run should be used for CPU related tasks.

I am working on an application that executes 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 a good and suitable use of async and await , or am I using it?

+34
c # asynchronous async-await
Feb 15 '13 at 14:29
source share
3 answers

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

If you are talking about using asynchronous operations, async / await works great for both I / O binding and CPU binding.

I think that MSDN documents have a slight bias towards creating asynchronous operations, in which case you want to use TaskCompletionSource (or similar) for I / O-bound and Task.Run (or similar) for CPU-related. Having created the original Task shell, it is best used by async and await .

In your specific example, this really comes down to how long LoadHtmlDocument takes. If you delete Task.Run , you will execute it in the same context that calls LoadPage (possibly in the user interface thread). The Windows 8 manuals indicate that any operation that takes more than 50 ms should be done async ... given that 50 ms on your development machine may be longer on the client machine ...

So, if you can guarantee that LoadHtmlDocument will run for less than 50 ms, you can simply 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 said:

 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 does not complete immediately (synchronously), then this (in this case) will cause LoadHtmlDocument be executed in the thread pool thread without explicitly calling Task.Run .

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

+29
Feb 15 '13 at 20:20
source share

There are several things to consider:

  • In a GUI application, you want to run as little code as possible in the user interface thread. In this case, overloading the operation with binding to another processor using Task.Run() is probably a good idea. Although users of your code can do it themselves if they want.
  • There is no UI thread in something like an ASP.NET application, and all you care about is performance. In this case, there is some overhead when using Task.Run() instead of directly running the code, but it should not be significant if the operation actually takes some time. (Also, there is some overhead when returning to a synchronization context, which is another reason you should use ConfigureAwait(false) for most await in your library code.)
  • If your isync method (which BTW should also be reflected in the name of the method, and not just its return type), people will expect it to not block the synchronization context thread, even for working with the CPU.

Weighing what I think using await Task.Run() is the right choice here. This has some overhead, but also some advantages that can be significant.

+16
Feb 15 '13 at 16:03
source share

Accordingly, await any operation that is asynchronous (i.e. represented by Task).

The key point is that for I / O operations, whenever possible, you want to use the provided method, which is inherently asynchronous rather than using Task.Run for the synchronous locking method. If you block a thread (even a thread thread stream) while doing IO, you are not using the real power of the await model.

Once you have created a Task that represents your operation, you don't care if this is related to the CPU or IO. For the caller, this is just some async operation, which should be await -ed.

+11
Feb 15 '13 at 15:39
source share



All Articles