Coder Perfect

Use Task when it’s appropriate. When you’re done, just async-await.

Problem

I’d like to hear your thoughts on the best architecture to employ while using Task.Run. Our WPF.NET 4.5 application has a sluggish user interface (with Caliburn Micro framework).

Essentially, I’m doing the following (extremely simplified code snippets):

public class PageViewModel : IHandle<SomeMessage>
{
   ...

   public async void Handle(SomeMessage message)
   {
      ShowLoadingAnimation();

      // Makes UI very laggy, but still not dead
      await this.contentLoader.LoadContentAsync();

      HideLoadingAnimation();
   }
}

public class ContentLoader
{
    public async Task LoadContentAsync()
    {
        await DoCpuBoundWorkAsync();
        await DoIoBoundWorkAsync();
        await DoCpuBoundWorkAsync();

        // I am not really sure what all I can consider as CPU bound as slowing down the UI
        await DoSomeOtherWorkAsync();
    }
}

According to the articles/videos I read/saw, await async does not always execute in a background thread, and you must wrap it in await Task to start working in the background. (async () =>…) Run(async () =>…). Although using async await does not interrupt the UI, it still runs on the UI thread, making it sluggish.

What’s the greatest location for Task.Run?

Should I just

The first answer, according to Ad (1), would be as follows:

public async void Handle(SomeMessage message)
{
    ShowLoadingAnimation();
    await Task.Run(async () => await this.contentLoader.LoadContentAsync());
    HideLoadingAnimation();
}

// Other methods do not use Task.Run as everything regardless
// if I/O or CPU bound would now run in the background.

The second answer, ad (2), would be as follows:

public async Task DoCpuBoundWorkAsync()
{
    await Task.Run(() => {
        // Do lot of work here
    });
}

public async Task DoSomeOtherWorkAsync(
{
    // I am not sure how to handle this methods -
    // probably need to test one by one, if it is slowing down UI
}

Asked by Lukas K

Solution #1

Take note of the following rules for working on a UI thread, which I’ve compiled on my blog:

There are two methods you should employ:

1) When possible, use ConfigureAwait(false).

For example, wait for MyAsync (). Instead to waiting for MyAsync(), use ConfigureAwait(false).

ConfigureAwait(false) instructs the await that the current context does not need to be resumed (in this case, “on the current context” means “on the UI thread”). You can’t do anything that implies you’re in the current context for the rest of that async method (after the ConfigureAwait) (e.g., update UI elements).

See my MSDN article Best Practices in Asynchronous Programming for additional information.

2) Make use of the task. To call CPU-bound methods, type run.

Task.Run should be used, but not in any code that you intend to reuse (i.e., library code). As a result, Task.Run is used to call the function rather than as part of the method’s implementation.

So here’s what pure CPU-bound tasks would look like:

// Documentation: This method is CPU-bound.
void DoWork();

Which you’d refer to as Task.Run:

await Task.Run(() => DoWork());

Methods that are both CPU and I/O bound should include an Async signature and documentation indicating their CPU-bound nature:

// Documentation: This method is CPU-bound.
Task DoWorkAsync();

This is also referred to as Task use. Run (due to the fact that it is largely CPU-bound):

await Task.Run(() => DoWorkAsync());

Answered by Stephen Cleary

Solution #2

One issue with your ContentLoader is that it works in a sequential manner internally. Parallelizing the work and then sychronizing at the end is a superior pattern.

public class PageViewModel : IHandle<SomeMessage>
{
   ...

   public async void Handle(SomeMessage message)
   {
      ShowLoadingAnimation();

      // makes UI very laggy, but still not dead
      await this.contentLoader.LoadContentAsync(); 

      HideLoadingAnimation();   
   }
}

public class ContentLoader 
{
    public async Task LoadContentAsync()
    {
        var tasks = new List<Task>();
        tasks.Add(DoCpuBoundWorkAsync());
        tasks.Add(DoIoBoundWorkAsync());
        tasks.Add(DoCpuBoundWorkAsync());
        tasks.Add(DoSomeOtherWorkAsync());

        await Task.WhenAll(tasks).ConfigureAwait(false);
    }
}

Obviously, this won’t work if some of the activities require data from previous processes, but it should improve overall throughput in the vast majority of cases.

Answered by Paul Hatcher

Post is based on https://stackoverflow.com/questions/18013523/when-correctly-use-task-run-and-when-just-async-await