1

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?

David Klempfner
  • 8,700
  • 20
  • 73
  • 153
  • Does this answer your question? [How to call asynchronous method from synchronous method in C#?](https://stackoverflow.com/questions/9343594/how-to-call-asynchronous-method-from-synchronous-method-in-c) – Mohammad Aghazadeh Jul 26 '23 at 05:47
  • [what is the difference between wait vs getawaiter getresult](https://stackoverflow.com/questions/36426937/what-is-the-difference-between-wait-vs-getawaiter-getresult) – Mohammad Aghazadeh Jul 26 '23 at 05:49
  • Where did you find this method? The `.Wait()` here may have been added later. – shingo Jul 26 '23 at 05:51
  • Be aware that using `.Wait()` can result in deadlocks in a UI application if the waited task needs to run on the UI thread. – JonasH Jul 26 '23 at 06:09

2 Answers2

2

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:

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
2

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.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
  • `HttpClient.GetAsync()` I assume would use `ConfigureAwait(false)` since it's framework code and they'd follow best practice. But even if it didn't, I don't understand how a deadlock could happen if you had `await _identityHttpClient.GetAsync(Uri)` instead of wrapping it in Task.Run(). Wouldn't the deadlock only happen if I had `_identityHttpClient.GetAsync(Uri).Result` (or `.Wait()`)? – David Klempfner Jul 27 '23 at 06:42
  • @DavidKlempfner yes, it is highly unlikely that a built-in asynchronous method would include such a bug. If it did, the resulting effect (deadlock) would be reported to Microsoft by so many developers that the bug would be fixed in the very next .NET version. You don't have to wrap the `HttpClient.GetAsync` in `Task.Run`. Also, yes, the deadlock can only happen with blocking `Wait`/`Result`, not with `await`. – Theodor Zoulias Jul 27 '23 at 10:36