我怎么能等待一个无效异步方法完成它的工作?
例如,我有一个这样的函数:
async void LoadBlahBlah()
{
await blah();
...
}
现在我想确保在继续其他地方之前所有东西都已加载。
我怎么能等待一个无效异步方法完成它的工作?
例如,我有一个这样的函数:
async void LoadBlahBlah()
{
await blah();
...
}
现在我想确保在继续其他地方之前所有东西都已加载。
你实际上不需要手动做任何事情,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()返回的对象类型
你不能真的等待一个void函数,所以LoadBlahBlah()不能为void
最佳实践是仅当函数是fire and forget方法时才将其标记为async void,如果你想等待,则应将其标记为async Task。
如果你仍然想等待,那么就像这样包装它。
最好的解决方案是使用async Task。你应该避免异步void有几个原因,其中之一是可组合性。
如果该方法不能返回Task(例如,它是一个事件处理程序),那么您可以在方法即将退出时使用SemaphoreSlim来获得方法信号。考虑在finally块中执行此操作。
做一个AutoResetEvent,调用函数,然后等待AutoResetEvent,然后在async void中设置它,当你知道它是完成的。
你也可以等待一个从void async返回的Task
我知道这是一个老问题,但这仍然是一个我一直遇到的问题,然而仍然没有明确的解决方案来正确地在异步无效签名方法中使用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方法中都没有任何代码会提示它们要阻塞,并且在它们启动后总是异步运行。
这意味着如果没有返回Task,将无法检查它是否完成。
正如预期的那样,它只启动异步运行的任务,然后它继续在代码中。 而async Task,首先它启动Task,然后返回它。
所以我想我的答案是永远不要使用async void,如果你需要知道任务何时完成,这就是async task的用途。
我已经阅读了所有的解决方案,它真的很复杂…最简单的解决方案是返回bool类型:
async bool LoadBlahBlah()
{
await blah();
return true;
}
存储或检查返回值不是强制的。你可以这样做:
await LoadBlahBlah();
... 如果出错,你可以返回false。
您可以简单地将返回类型更改为Task并调用await Task。函数末尾的CompletedTask,例如:
async Task MyFunction() {
await AnotherFunction();
await Task.CompletedTask;
}
我发现这比将整个函数体包装在对task的调用中更简单。run(() =>{…});。