Async-await Task.Run vs HttpClient.GetAsync

vondip picture vondip · Nov 23, 2012 · Viewed 21.6k times · Source

I'm new to c# 5's async feature. I'm trying to understand the difference between these two implementations:

Implementation 1:

private void Start()
{
    foreach(var url in urls)
    {
        ParseHtml(url);
    }
}

private async void ParseHtml(string url)
{
    var query = BuildQuery(url); //BuildQuery is some helper method
    var html = await DownloadHtml(query);
    //...
    MyType parsedItem = ParseHtml(html);
    SaveTypeToDB(parsedItem);
}

private async Task<string> DownloadHtml(string query)
{
    using (var client = new HttpClient())
    try
    {
        var response = await client.GetAsync(query);
        return (await response.Content.ReadAsAsync<string>());
    }
    catch (Exception ex)
    {
        Logger.Error(msg, ex);
        return null;
    }
}

Implementation 2:

private void DoLoop()
{
    foreach(var url in urls)
    {
        Start(url);
    }
}

private async void Start(url)
{
    await Task.Run( () => ParseHtml(url)) ;
}

private void ParseHtml(string url)
{
    var query = BuildQuery(url); //BuildQuery is some helper method
    var html = DownloadHtml(query);
    //...
    MyType parsedItem = ParseHtml(html);
    SaveTypeToDB(parsedItem);
}

private string DownloadHtml(string query)
{
    using (var client = new WebClient())
    {
        try
        {
            return client.DownloadString(query);
        }
        catch (Exception ex)
        {
            Logger.Error(msg, ex);
            return null;
        }
    }
}

I'd rather use the second implementation as it will require less 'async' signatures on methods in my code. I'm trying to understand what's the benefit of using the HttpClient class vs using a new Task and awaiting it instead?

Is there any difference between the two implementations?

Answer

Jon Skeet picture Jon Skeet · Nov 23, 2012

I'd rather use the second implementation as it will require less 'async' signatures on methods in my code.

That sounds like a very odd justification. You're trying to execute fundamentally "somewhat asynchronously" - so why not make that clear?

Is there any difference between the two implementations?

Absolutely. The second implementation will tie up a thread while WebClient.DownloadString blocks, waiting for the request to complete. The first version doesn't have any blocked threads - it relies on a continuation to fire when the request finishes.

Additionally, consider your Logger.Error call. In the async version, that will still execute in the context of the original calling code. So if this is in, say, a Windows Forms UI, you'll still be on the UI thread, and you can access UI elements etc. In the second version, you'll be executing in a thread pool thread, and would need to marshal back to the UI thread to update the UI.

Note that your async void method almost certainly shouldn't be async void. You should only make an async method return void for the sake of complying with event handler signatures. In all other cases, return Task - that way the caller can see when your task has finished, handle exceptions etc.

Also note that you don't need to use HttpClient for asynchrony - you could use WebClient.DownloadStringTaskAsync instead, so your final method could become:

private async Task<string> DownloadHtmlAsync(string query)
{
    using (var client = new WebClient())
    {
        try
        {
            return await client.DownloadStringTaskAsync(query);
        }
        catch (Exception ex)
        {
            Logger.Error(msg, ex);
            return null;
        }
    }
}