等待一个无效的异步方法

Posted

技术标签:

【中文标题】等待一个无效的异步方法【英文标题】:Wait for a void async method 【发布时间】:2012-11-18 04:01:53 【问题描述】:

如何等待void async 方法完成其工作?

例如,我有如下函数:

async void LoadBlahBlah()

    await blah();
    ...

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

【问题讨论】:

【参考方案1】:

最好的做法是标记函数async void,只有当它是fire and forget方法时,如果你想等待,你应该把它标记为async Task

如果你还想等待,那么像这样包装它await Task.Run(() => blah())

【讨论】:

blah 显然是在返回任务并且有异步签名为什么你要在没有等待的情况下调用它。甚至 VS 都会给你警告。 await Task.Run(() => An_async_void_method_I_can_not_modify_now()) await Task.Run(() => blah()) 具有误导性。这不会等待异步函数 blah 的完成,它只是等待任务的(微不足道的)创建,并在 blah() 完成之前继续。 @RohitSharma,这对于您的示例是正确的,但 Thread.Sleep 不是异步的。这个问题是关于等待async void 函数,比如async void blah() Task.Delay(10000); @RohitSharma 来自文档 如果调用者不希望它们是异步的,异步 void 方法可能会造成严重破坏。当返回类型为 Task 时,调用者知道它正在处理未来的操作;当返回类型为 void 时,调用者可能会认为方法在返回时已完成。 所以 JonathanLidbeck 是对的。【参考方案2】:

如果您可以将函数的签名更改为async Task,那么您可以使用here提供的代码

【讨论】:

【参考方案3】:

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

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

【讨论】:

【参考方案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

【讨论】:

我想等待LoadBlahBlah()完成,而不是blah() 不幸的是 async void 方法不会暂停执行(与 async Task 方法不同)【参考方案5】:

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

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

【讨论】:

【参考方案6】:

我知道这是一个老问题,但这仍然是我一直遇到的问题,但在异步 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 正在按预期工作,如果您需要来自内部 Task 的反馈,那么您需要使用 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 方法中没有任何代码会提示它们阻塞,并且在它们启动后总是异步运行。

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

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

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

【讨论】:

以上是关于等待一个无效的异步方法的主要内容,如果未能解决你的问题,请参考以下文章

等待 dispatch_semaphore 以等待许多异步任务完成的正确方法

传递异步方法真的需要等待/异步模式吗? [复制]

如何等待异步方法的回调返回值?

如何等待异步方法的回调返回值?

异步/等待不等待返回值

在异步方法中省略异步和等待[重复]