“异步任务”与“返回Task.Run”

时间:2020-06-17 16:00:22

标签: c# asynchronous async-await

我是异步/等待C#模型的新手,试图了解这两个选项是否本质上是同一件事:

public Task LongRunningMethod()
{
    return Task.Run(async () =>
    {
        await DoStuff();
    });
}

//then call it
await LongRunningMethod();

还有这个

public async Task LongRunningMethod()
{
    await DoStuff();
}

//then call it
await LongRunningMethod();

我在想第一种方法将耗尽池中的一个额外线程...并且还将一个Task封装为一个额外Task。还是我错了?

3 个答案:

答案 0 :(得分:7)

Task.Run将其委托放入线程池。这会导致两件事:

  1. 第一个异步DoStuff之前await中的任何代码都将在线程池线程上运行,而不是on the calling thread
  2. DoStuff中的代码将在线程池上下文中运行,而不是using whatever context was current from the calling thread

大多数,在Task.Run中进行异步工作是一个错误。但这有时很有用,例如,如果DoStuff在异步操作之前做了一些繁重的计算工作,那么Task.Run可用于将工作移出UI线程。

答案 1 :(得分:2)

它们非常相似,但是DoStuff有可能同步完成;如果发生这种情况,您的第二个方法(仅await)将阻塞多长时间,而第一个方法(Task.Run)将始终不完整地返回给调用方(展开其堆栈并安排一个连续),并在池线程上运行阻塞工作。

在不同情况下,两种选择都是理想的!

但是,还有第三种选择,它表达了准确的意图,即“在其他地方运行其余部分” 只要您没有{{1 }}:

SyncContext

如果没有public async Task LongRunningMethod() { await Task.Yield(); await DoStuff(); // possibly with .ConfigureAwait(false) } ,则其功能与SyncContext版本相似(意味着:它将始终以不完整的形式返回给调用者,并在池上运行Task.Run ),只是:没有实际的DoStuff位。但是,如果 Task.Run:它将回到 same 上下文,这很尴尬,不幸的是没有{{1 }} SyncContext,因此在这种情况下 您必须使用ConfigureAwait(false)或类似的语言。

答案 2 :(得分:1)

方法名称传达了这样做的原因,即“ LongRunning”。您可以将创建标志@Echo Off SetLocal EnableExtensions DisableDelayedExpansion Set "Reg=%__AppDir__%reg.exe" Set "Find=%__AppDir__%find.exe" Set "Key=HKCU\Software\R-core\R" Set "Val=InstallPath" Set "RDir=" For /F "EOL=H Tokens=2*" %%G In ( '"%Reg% Query "%Key%" /S /F "%Val%" /V 2>NUL|%Find% "\""') Do Set "RDir=%%~H" If Not Defined RDir For /F "EOL=H Tokens=2*" %%G In ( '"%Reg% Query "HKLM%Key:~4%" /S /F "%Val%" /V 2>NUL|%Find% "\""' ) Do Set "RDir=%%~H" If Not Defined RDir GoTo :EOF If "%PROCESSOR_ARCHITECTURE:~-2%" == "86" ( If Not Defined PROCESSOR_ARCHITEW6432 (Set "RDir=%RDir%\bin\i386" ) Else Set "RDir=%RDir%\bin\x64") Else Set "RDir=%RDir%\bin\x64" Where /Q "%RDir%":"RScript.exe" && (Set "RDir=%RDir%\RScript.exe") || Set "RDir=" If Defined RDir Echo Your default RScript.exe absolute path is %RDir% & Pause 传递给LongRunning(此示例没有这样做),并且作为纯实现线程,运行时将为此分配一个单独的线程。

根据情况,即使您的第二个示例也不完全正确。如果这是一个库,那么它实际上应该是Task.Run()