等待 async void 方法

问题描述 投票:0回答:8

如何等待

async void
方法完成其工作?

例如,我有一个如下所示的函数:

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

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

c# asynchronous async-await void
8个回答
342
投票

最佳实践是仅当函数是即发即忘方法时才将其标记为

async void
,如果您想等待,则应将其标记为
async Task

如果你还想等待,就这样包起来吧

await Task.Run(() => blah())


62
投票

如果您可以将函数的签名更改为

async Task
那么您可以使用此处

提供的代码

33
投票

最好的解决方案是使用

async Task
。出于多种原因,您应该避免
async void
,其中之一是可组合性。

如果方法不能返回

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


5
投票

我知道这是一个老问题,但这仍然是我不断遇到的一个问题,但在 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 创建一个内部任务(就像异步任务一样),但由于签名不支持返回该内部任务

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

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

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

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

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 的用途。


4
投票

您实际上不需要手动执行任何操作,

await
关键字会暂停函数执行,直到
blah()
返回。

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
是对象类型
blah()
返回

你不能真正

await
void
函数,所以
LoadBlahBlah()
不能是
void


0
投票

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

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


-1
投票

您可以简单地将返回类型更改为 Task 并在函数末尾调用

await Task.CompletedTask
,例如:

async Task MyFunction() {
    await AnotherFunction();

    await Task.CompletedTask;
}

我发现这比将整个函数体包装在对

Task.Run(() => { ...  });
的调用中更简单。


-2
投票

我已经阅读了该线程的所有解决方案,它真的很复杂......最简单的解决方案是返回类似 bool 的东西:

async bool LoadBlahBlah()
{
    await blah();
    return true;
}

不强制存储或检查返回值。你可以这样做:

await LoadBlahBlah();

...如果出现问题,您可以返回

false

© www.soinside.com 2019 - 2024. All rights reserved.