They are very similar, but there is a chance that DoStuff
could complete synchronously; if that happens, your second (await
only) method would block for however long that takes, where-as the first (Task.Run
) would always return incomplete to the caller (unwinding their stack and scheduling a continuation), and have the blocking work run on the pool thread.
Both options can be desirable, in different scenarios!
However, there is a third option, which expresses exactly the intent of "run the rest somewhere else" as long as you don't have a SyncContext
:
public async Task LongRunningMethod()
{
await Task.Yield();
await DoStuff(); // possibly with .ConfigureAwait(false)
}
If there is no SyncContext
, this is functionally similar to the Task.Run
version (meaning: it will always return to the caller as incomplete, and run the DoStuff
starting on the pool), just: without the actual Task.Run
bits. However, if there is a SyncContext
: it will go back onto the same context, which is ... awkward, and unfortunately there is no ConfigureAwait(false)
for YieldAwaitable
, so in that case you'd have to use Task.Run
or similar.