Is there a reason to run a Task within another Task?

329 views Asked by At

I found this method in an ASP.NET application:

public void LogoutAllSessions()
{
    Task.Run(async () => await _identityHttpClient.GetAsync(Uri)).Wait();

    // Could this be rewritten as this:?
    // _identityHttpClient.GetAsync(Uri).GetAwaiter().GetResult();
}

Is this just running a task within another task and blocking the thread while it waits for the outer task to finish?

I was told it was written like this because updating it to just use plain async/await would involve changing too many methods up the stack.

Would _identityHttpClient.GetAsync(Uri).GetAwaiter().GetResult(); do the same thing as Task.Run(async () => await _identityHttpClient.GetAsync(Uri)).Wait();?

Would one be faster than the other?

2

There are 2 answers

2
Theodor Zoulias On BEST ANSWER

There is indeed a reason to wrap the asynchronous method GetAsync in Task.Run before waiting it synchronously with .Wait() or .GetAwaiter().GetResult(). The reason is to prevent a deadlock in case the GetAsync is implemented with async/await, there is a SynchronizationContext installed on the current thread (typical for GUI applications), there is an await inside the GetAsync not configured with .ConfigureAwait(false), and the awaitable (Task) is not already completed at the await point. Under these conditions the deadlock happens consistently (without Task.Run), you'll observe it immediately during the testing (the application will hang), and you'll be forced to fix it. Wrapping the call with Task.Run is one way to fix it, because the Task.Run invokes the asynchronous method on the ThreadPool, where there is no SynchronizationContext installed. If you remove the Task.Run and your application does not hang, most likely the Task.Run is redundant.

The effect that a redundant Task.Run has on performance is negligible. It can be either beneficial or detrimental, but the difference will be minuscule.

To understand the reason for the deadlock, you could take a look at this question: An async/await example that causes a deadlock.

1
Guru Stron On

Would _identityHttpClient.GetAsync(Uri).GetAwaiter().GetResult(); do the same thing as Task.Run(async () => await _identityHttpClient.GetAsync(Uri)).Wait();?

It depends. In presence synchronization context tied to a specific thread (like the UI context is), or allowing one thread in at a time (like "classic" ASP.NET one) depending on the _identityHttpClient.GetAsync implementation the first one can lead to deadlocks.

For more info on the topic - check out the Don't Block on Async Code by Stephen Cleary

Would one be faster than the other?

Not that important in this case I would argue, the second one offloads to thread pool which can be slower or faster depending on the method implementation, synchronization context presence and thread pool state, but usually it is not that significant. In terms of overall app performance (throughput) the best option would be to make the method async and await the async calls (and "bubble up" the async).

Read also: