191

我怎样才能等待一个void async方法完成它的工作?

例如,我有如下功能:

async void LoadBlahBlah()
{
    await blah();
    ...
}

现在我想确保所有内容都已加载,然后再继续其他地方。

4

6 回答 6

302

最佳实践是仅在函数async void为 fire and forget 方法时才将其标记,如果要等待,则应将其标记为async Task.

如果您仍然想等待,请像这样包装它await Task.Run(() => blah())

于 2012-11-30T02:17:28.257 回答
62

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

于 2012-11-30T00:36:41.357 回答
30

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

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

于 2012-11-30T02:21:20.273 回答
5

You don't really need to do anything manually, await keyword pauses the function execution until blah() returns.

private async void SomeFunction()
{
     var x = await LoadBlahBlah(); <- Function is not paused
     //rest of the code get's executed even if LoadBlahBlah() is still executing
}

private async Task<T> LoadBlahBlah()
{
     await DoStuff();  <- function is paused
     await DoMoreStuff();
}

T is type of object blah() returns

You can't really await a void function so LoadBlahBlah() cannot be void

于 2012-11-30T01:24:26.733 回答
1

执行 AutoResetEvent,调用该函数,然后等待 AutoResetEvent,然后在您知道它完成时将其设置在 async void 内。

您还可以等待从您的 void async 返回的任务

于 2013-08-23T20:34:50.120 回答
1

我知道这是一个老问题,但这仍然是我一直遇到的一个问题,但是在 async void 签名方法中使用 async/await 时仍然没有明确的解决方案可以正确地做到这一点。

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

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

void LoadBlahBlah()
{
    blah().Wait(); //this blocks
}

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

async void LoadBlahBlah()
{
    await blah(); //this does not block
}

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

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

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

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

编辑:我做了一些示例代码并对其进行反编译以查看实际发生的情况。

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

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

变成(编辑:我知道正文代码不在此处,而是在状态机中,但状态机基本相同,所以我没有费心添加它们)

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;
}

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

意思是没有返回的任务,就没有办法检查它是否完成。

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

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

于 2020-01-15T15:19:43.990 回答