等待void异步方法

MBZ*_*MBZ 138 c# asynchronous

我怎么能等待void async方法完成它的工作?

例如,我有一个如下功能:

async void LoadBlahBlah()
{
    await blah();
    ...
}
Run Code Online (Sandbox Code Playgroud)

现在我想确保在继续其他地方之前已经加载了所有内容.

Roh*_*rma 199

最佳做法是async void仅在火灾和忘记方法时标记功能,如果要等待,则应将其标记为async Task.

如果您仍想等待,请将其包装起来 await Task.Run(() => blah())

  • `await Task.Run(() => blah())` 具​​有误导性。这不会等待异步函数“blah”的完成,它只是等待任务的(简单)创建,并在“blah()”完成之前立即继续。 (7认同)
  • 等待Task.Run(()=> An_async_void_method_I_can_not_modify_now()) (3认同)
  • @RohitSharma,这对于您的示例来说是正确的,但“Thread.Sleep”不是异步的。这个问题是关于等待“async void”函数,比如“async void blah() { Task.Delay(10000); }` (3认同)

小智 58

如果您可以更改函数的签名,async Task则可以使用此处提供的代码


Ste*_*ary 25

最好的解决方案是使用async Task.你应该避免async void出于几个原因,其中一个原因是可组合性.

如果无法返回该方法Task(例如,它是一个事件处理程序),那么您可以SemaphoreSlim在它即将退出时使用该方法信号.考虑在一个finally块中这样做.


Dro*_*roa 7

我知道这是一个老问题,但这仍然是我不断遇到的问题,但在 async void 签名方法中使用 async/await 时仍然没有明确的解决方案来正确执行此操作。

但是,我注意到 .Wait() 在 void 方法内正常工作。

由于 async void 和 void 具有相同的签名,因此您可能需要执行以下操作。

void LoadBlahBlah()
{
    blah().Wait(); //this blocks
}
Run Code Online (Sandbox Code Playgroud)

令人困惑的是 async/await 不会阻塞下一个代码。

async void LoadBlahBlah()
{
    await blah(); //this does not block
}
Run Code Online (Sandbox Code Playgroud)

当您反编译代码时,我的猜测是 async void 创建一个内部任务(就像异步任务一样),但由于签名不支持返回该内部任务

这意味着在内部 async void 方法仍然能够“等待”内部异步方法。但外部无法知道内部任务何时完成。

所以我的结论是 async void 正在按预期工作,如果您需要来自内部任务的反馈,那么您需要使用 async Task 签名。

希望我的漫谈对任何也在寻找答案的人来说都是有意义的。

编辑:我制作了一些示例代码并将其反编译以查看实际发生的情况。

static async void Test()
{
    await Task.Delay(5000);
}

static async Task TestAsync()
{
    await Task.Delay(5000);
}
Run Code Online (Sandbox Code Playgroud)

变成(编辑:我知道主体代码不在这里,而是在状态机中,但状态机基本上是相同的,所以我没有费心添加它们)

private static void Test()
{
    <Test>d__1 stateMachine = new <Test>d__1();
    stateMachine.<>t__builder = AsyncVoidMethodBuilder.Create();
    stateMachine.<>1__state = -1;
    AsyncVoidMethodBuilder <>t__builder = stateMachine.<>t__builder;
    <>t__builder.Start(ref stateMachine);
}
private static Task TestAsync()
{
    <TestAsync>d__2 stateMachine = new <TestAsync>d__2();
    stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
    stateMachine.<>1__state = -1;
    AsyncTaskMethodBuilder <>t__builder = stateMachine.<>t__builder;
    <>t__builder.Start(ref stateMachine);
    return stateMachine.<>t__builder.Task;
}
Run Code Online (Sandbox Code Playgroud)

AsyncVoidMethodBuilder 或 AsyncTaskMethodBuilder 实际上在 Start 方法中都没有任何代码来暗示它们会阻塞,并且在启动后始终会异步运行。

这意味着如果没有返回任务,就无法检查它是否完成。

正如预期的那样,它只启动异步运行的任务,然后在代码中继续。和异步任务,首先它启动任务,然后返回它。

所以我想我的答案是永远不要使用 async void,如果您需要知道任务何时完成,这就是 async Task 的用途。