26

I just came across some code like:

var task = Task.Run(async () => { await Foo.StartAsync(); });
task.Wait();

(No, I don't know the inner-workings of Foo.StartAsync()). My initial reaction would be get rid of async/await and rewrite as:

var task = Foo.StartAsync();
task.Wait();

Would that be correct, or not (again, knowing nothing at all about Foo.StartAsync()). This answer to What difference does it make - running an 'async' action delegate with a Task.Run ... seems to indicate there may be cases when it might make sense, but it also says "To tell the truth, I haven't seen that many scenarios ..."

Community
  • 1
  • 1
Ðаn
  • 10,934
  • 11
  • 59
  • 95

3 Answers3

24

Normally, the intended usage for Task.Run is to execute CPU-bound code on a non-UI thread. As such, it would be quite rare for it to be used with an async delegate, but it is possible (e.g., for code that has both asynchronous and CPU-bound portions).

However, that's the intended usage. I think in your example:

var task = Task.Run(async () => { await Foo.StartAsync(); });
task.Wait();

It's far more likely that the original author is attempting to synchronously block on asynchronous code, and is (ab)using Task.Run to avoid deadlocks common in that situation (as I describe on my blog).

In essence, it looks like the "thread pool hack" that I describe in my article on brownfield asynchronous code.

The best solution is to not use Task.Run or Wait:

await Foo.StartAsync();

This will cause async to grow through your code base, which is the best approach, but may cause an unacceptable amount of work for your developers right now. This is presumably why your predecessor used Task.Run(..).Wait().

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

Mostly yes.

Using Task.Run like this is mostly used by people who don't understand how to execute an async method.

However, there is a difference. Using Task.Run means starting the async method on a ThreadPool thread.

This can be useful when the async method's synchronous part (the part before the first await) is substantial and the caller wants to make sure that method isn't blocking.

This can also be used to "get out of" the current context, for example where there isn't a SynchronizationContext.

i3arnon
  • 113,022
  • 33
  • 324
  • 344
  • 1
    @Dan I wouldn't say that. Also, it's less an issue for the method itself. It may be an issue for the caller. If your thread is "special" (UI thread, or waiting for requests) and you want to offload as much as you can to other threads you may use `Task.Run`. – i3arnon Sep 15 '15 at 16:56
  • so the recommended way should be async await inside Task.Run as well? – Ehsan Sajjad Sep 15 '15 at 16:59
  • 1
    @EhsanSajjad that's beside the question. It's not needed in this case, but it's reasonable to use it to reduce confusion. I think `Task.Run(() => Foo.StartAsync())` is clearer – i3arnon Sep 15 '15 at 17:01
  • @i3arnon i am still unclear when to put it and when not, ``Task.Run()`` is itself async call right? – Ehsan Sajjad Sep 15 '15 at 17:02
  • 1
    @EhsanSajjad when to put what where? the async and await inside the `Task.Run` are never needed. `Task.Run` itself returns a task you can await. – i3arnon Sep 15 '15 at 17:04
  • i was talking about async and awaiit operator @i3arnon – Ehsan Sajjad Sep 15 '15 at 17:05
0

It's worth noting that your method has to be marked async to be able to use the await keyword.

The code as written seems to be a workaround for running asynchronous code in a synchronous context. While I wouldn't say you should never ever do this, using async methods is preferable in almost every scenario.

// use this only when running Tasks in a synchronous method
// use async instead whenever possible
var task = Task.Run(async () => await Foo.StartAsync());
task.Wait();

Async methods, like your example of Foo.StartAsync(), should always return a Task object. This means that using Task.Run() to create another task is usually redundant in an async method. The task returned by the async method can simply be awaited by using the await keyword. The only reason you should use Task.Run() is when you are performing CPU-bound work that needs to be performed on a separate thread. The task returned by the async method can simply be awaited by using the await keyword. You can read more in depth details in Microsoft's guide to async programming.

In an async method, your code can be as simple as:

await Foo.StartAsync();

If you want to perform some other work while the task is running, you can assign the function to a variable and await the result (task completion) later.

For example:

var task = Foo.StartAsync();
// do some other work before waiting for task to finish
Bar();
Baz();
// now wait for the task to finish executing
await task;

With CPU-bound work that needs to be run on a separate thread, you can use Task.Run(), but you await the result instead of using the thread blocking Task.Wait():

var task = Task.Run(async () => await Foo.StartAsync());
// do some other work before waiting for task to finish
Bar();
Baz();
// now wait for the task to finish executing
await task;