I've recently come across this code in a WinForm application and I can't figure if there is any reason to run async
code inside a Task.Run
that is awaited.
public async Task SaveStuff()
{
await Task.Run(() => SaveStuffAsync().ConfigureAwait(false));
await Task.Run(() => SendToExternalApiAsync().ConfigureAwait(false));
}
private async Task SaveStuffAsync()
{
await DbContext.SaveChangesAsync().ConfigureAwait(false);
}
private async Task SendToExternalApiAsync()
{
// some async code that is awaited with ConfigureAwait(false);
}
Wouldn't this code do the exact same thing without the Task.Run?
public async Task SaveStuff()
{
await SaveStuffAsync().ConfigureAwait(false);
await SendToExternalApiAsync().ConfigureAwait(false);
}
Wouldn't this code do the exact same thing without the Task.Run?
If the code inside the async method is actually asynchronous, it will not really make a difference. The Task
would be executed on the threadpool, so it may take some more resources to execute. From the calling thread point of view, you would not notice the difference.
If, however, the code inside your async method is (not a)synchronous, you will notice a difference. Consider the following method:
private async Task DoWorkNotReallyAsync()
{
for (int i = 0; i < aVeryLargeNumber; i++)
{
DoSynchronousComputation();
}
}
The above method has an async signature, but will actually be run synchronously, thus blocking the calling thread while it executes. Wrapping the call in a Task.Run
will schedule the execution to the threadpool. Therefore wrapping tasks in Task.Run
might be useful if you want to be certain that a call to the async method will not block the current thread.
The methods called in your example do look truly asynchronous, so I wouldn't see a reason to wrap those tasks in Task.Run
.
The fact that a method returns a Task
doesn't mean it yields back immediately. It might have have some time/CPU consuming setup before an I/O operation, for example.
For that reason, it is usual to see, on client UIs, everything outside of the UI being called inside Task.Run
.
That being said, this is not such a case:
public async Task SaveStuff()
{
await Task.Run(() => SaveStuffAsync().ConfigureAwait(false));
await Task.Run(() => SendToExternalApiAsync().ConfigureAwait(false));
}
That causes one extra execution in the UI thread only to schedule work on the thread pool.
this would be more acceptable:
public async Task SaveStuff()
{
await Task.Run(
async () =>
{
await SaveStuffAsync();
await SendToExternalApiAsync();
});
}
There's no need to invoke ConfigureAwait(false)
because it's guaranteed to not have a SynchronizationContext
.
The difference between this and your last snippet is where and how that code is being invoked,
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With