问题
I'm new to async/await c# model and trying to understand if these two options are essentially the same thing:
public Task LongRunningMethod()
{
return Task.Run(async () =>
{
await DoStuff();
});
}
//then call it
await LongRunningMethod();
and this
public async Task LongRunningMethod()
{
await DoStuff();
}
//then call it
await LongRunningMethod();
I'm thinking the 1st way will use up an extra thread from the pool... And it also wraps a Task into an extra Task. Or am I wrong?
回答1:
Task.Run
will queue its delegate to the thread pool. This causes two important things:
- Any code in
DoStuff
before the first asynchronousawait
will run on a thread pool thread, instead of on the calling thread. - The code in
DoStuff
will run in a thread pool context, instead of using whatever context was current from the calling thread.
Most of the time, doing asynchronous work in Task.Run
is a mistake. But it is sometimes useful, say if DoStuff
does some heavy computational work before it acts asynchronously, then Task.Run
could be used to move that work off of a UI thread.
回答2:
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.
回答3:
The method name conveys the reason why it is done like that, "LongRunning". You can pass a creation flag LongRunning
to Task.Run()
(which this sample doesn't) and as a pure implementation thread, runtime will allocate a separate thread for that.
Even your second example isn't completely correct, depending on circumstances. If this is a library, then it really should be await DoStuff().ConfigureAwait(false);
.
来源:https://stackoverflow.com/questions/62433284/async-task-vs-return-task-run