"await"关键字术语背后的原因是什么?



如果同步调用一个函数,程序流仍然需要等待响应,那么这里有什么区别?

假设您有一些任务需要完成。A和B,每个都由几个步骤组成,A1、A2、B1、B2等等。A和B是独立的,每个任务的步骤需要按顺序运行。

因此,执行任务的典型方式是A1, A2, B1, B2B1, B2, A1, A2。如果任务大多是CPU绑定的,那么这就可以正常工作。但是,如果A1是IO操作,如果先安排,我们将浪费CPU时间。因此,在这种情况下,最好按A1, B1, A2, B2的顺序运行。在现代计算机上,IO操作可能比CPU指令慢很多数量级,因此解放CPU去做其他事情可能是一个重大的胜利,尤其是在试图编写响应UI时。

但现在我们已经使我们的程序编写和推理变得更加复杂,"程序顺序"不再与执行顺序匹配。

实现这一点的经典方法是使用回调,它可能看起来像:

public void A(){
A1(A2, A1FailureHandler);
}
public void A1(Action<A1Result> callback, Action<Exception> failCallback){
...
}
public void A2(A1Result r){ ... }
public void A1FailureHandler(Exception ex){ ... }

这往往会使代码的编写和理解变得更加复杂,尤其是当您的任务包含更多步骤时。解决方案是让编译器担心它!当使用async关键字时,它允许我们使用更熟悉的结构编写上面的代码

public async void A(){
try{
var a1Result = await A1();
A2(a1Result);
}
catch(Exception e){...}
}
public Task A1(){...}
public void A2(A1Result r){...}

在后台,该方法将被重写为一个类,所有的局部变量都被更改为字段,a方法被更改为一个大型switch语句,用于跟踪它当前运行的步骤。这让我们可以按照逻辑程序顺序编写东西,并让编译器和运行时担心实际的执行顺序。然而,抽象往往是泄漏的,异步程序可能更难理解和调试。

来自等待运算符语言引用。

await运算符挂起对封闭异步的求值方法,直到由其操作数表示的异步操作完成。当异步操作完成时,await运算符返回操作的结果(如果有的话(。当等待运算符应用于表示已已完成的操作,它返回操作的结果立即而不暂停封闭方法。await运算符不会阻塞评估异步方法的线程。当wait运算符挂起封闭的异步方法时控件返回到方法的调用方。

因此await关键字意味着下面的指令将等待上一条异步指令的完成。如果没有wait关键字,执行顺序将无法保证,后续指令可能会在异步任务终止之前执行。

最新更新