0

I've been trying to figure out why the UI was blocking from a ViewModel method, and realized that this part of the code:

await Task.WhenAll(getOutput1(), getOutput2());

was the problem. I managed to unblock the UI by using:

await Task.WhenAll(Task.Run(() => getOutput1()), Task.Run(() => getOutput2()));

getOutput1() and getOutput2() are both async with Task return types in the ViewModel, and the code is called from the View.

What's the difference with calling Task.WhenAll when I call Task.Run() and just directly supplying the task?

Tyress
  • 3,573
  • 2
  • 22
  • 45
  • 1
    If `getOutput1` and `getOutput2` both "really" async then first option must work properly. can you show those methods? Notice that async method will be executed synchronously if you haven't read "IO" operations – Fabio Oct 19 '16 at 07:53
  • 5
    Entirely depends on the nature of `getOutput1` and `getOutput2`. If they do significant amounts of work without invoking any async behaviour themselves, that work is done on the original thread in version 1 of your code. – Damien_The_Unbeliever Oct 19 '16 at 07:55
  • @Damien_The_Unbeliever actually you are right, I only invoke async behavior when I schedule on a UI thread within the method. Thanks for the insight – Tyress Oct 20 '16 at 00:40

2 Answers2

2

If the methods are pure async operations then you should not use Task.Run while calling them from the UI thread and it will work properly.

However if the methods are also involved in a long running CPU bound work, the UI thread won't be blocked while the async I/O operation is executing but will be while the CPU bound work is.

In this scenario you need the use Task.Run while you are calling the methods even though the methods already look like a async ones, because the methods are actually a combination of synchronous and asynchronous operations and you don't want to block the UI while the synchronous work is done.

Another option instead of using Task.Run from the ViewModel is to use ConfigureAwait(false) while awaiting the I/O async operations in your methods, doing that will inform to the rest of the method after the await part that it does not need the original context it had (which is probably the UI one) and that the rest of the method can also be executed in another ThreadPool thread instead.

Please refer this question for more info about async and await methods that combine I/O and CPU bound works.

Community
  • 1
  • 1
YuvShap
  • 3,825
  • 2
  • 10
  • 24
2

What's the difference with calling Task.WhenAll when I call Task.Run() and just directly supplying the task?

Calling the methods directly will invoke them on the UI thread. Calling them from within Task.Run will invoke them on a thread pool thread.

Conclusion: getOutput1 and/or getOutput2 are not actually asynchronous. (It is entirely possible for a method to return Task - and thus appear asynchronous - but in reality just block synchronously).

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810