Async和Await的工作原理

本文关键字:工作 Await Async | 更新日期: 2023-09-27 17:49:36

我正在努力了解Async和Await是如何工作的。如何在程序中控制行程。这是我试图理解的代码。

public async Task MyMethod()
{
    Task<int> longRunningTask = LongRunningOperation();
    //indeed you can do independent to the int result work here 
    MySynchronousMethod();
    //and now we call await on the task 
    int result = await longRunningTask;
    //use the result 
    Console.WriteLine(result);
}
public async Task<int> LongRunningOperation() // assume we return an int from this long running operation 
{
    await Task.Delay(5000); //5 seconds delay
    return 1;
}
private void Button_Click_3(object sender, RoutedEventArgs e)
{
     MyMethod();
}

单击按钮时,将调用MyMethod(),并从MyMethod调用LongRunningOperation(),这需要5秒才能完成。所以我的问题是

  • 这条线是什么意思

    任务longRunningTask=LongRunningOperation((;

  • int result = await longRunningTask; 是什么

上述线路可以提交,我们可以像一样构建一条线路

Task<int> longRunningTask = await LongRunningOperation();

int result = await longRunningTask;

请有人向我解释一下我不清楚的上述代码

1( 如果longRunningOperation尚未完成并且仍在运行,MyMethod((将返回其调用方法,因此主线程不会被阻塞。当longRunningOperation完成时,ThreadPool中的一个线程(可以是任何线程(将返回到MyMethod((的前一状态并继续执行(在本例中,将结果打印到控制台(。

第二种情况是longRunningOperation已经完成执行,并且结果可用。当到达wait-longRunningOperation时,编译器知道它有结果,并将继续在同一个线程上执行代码。(在这种情况下,将结果打印到控制台(。

第1点对我来说根本不清楚,就像的"if the longRunningOperation hasn't finished and is still running, MyMethod() will return to its calling method"语句一样

如果可能的话,请更详细地解释这一点。感谢

//this is pseudocode
async Method()
{
    code;
    code;
    await something; 
    moreCode;
}

当调用Method时,它执行其内容(code;行(,直到await something;。在这一点上,something;被激发,方法结束时就像return;一样。

something;执行它需要的操作,然后返回。

something;返回时,执行返回到Method并从await继续,执行moreCode;

以一种更为示意的方式,以下是发生的情况:

  1. 方法被调用
  2. 执行code;
  3. 执行something;,流程返回到调用Method的点
  4. 执行继续进行Method调用之后的内容
  5. something;返回时,流在Method内部返回
  6. 执行moreCode;Method本身结束(是的,可能还有其他await,依此类推(

Async和Await的工作原理

我的博客上有一篇async简介,你可能会觉得很有帮助。

此代码:

int result = await LongRunningOperation();

本质上与此代码相同:

Task<int> resultTask = LongRunningOperation();
int result = await resultTask;

所以,是的,LongRunningOperation是由该方法直接调用的。

await运算符被传递一个已经完成的任务时,它将提取结果并继续执行该方法(同步(。

await运算符被传递一个不完整的任务(例如,LongRunningOperation返回的任务将不完整(时,默认情况下,await将捕获当前上下文并从该方法返回一个不完成的任务。

稍后,当await任务完成时,该方法的其余部分被安排在该上下文中运行。

这个"上下文"是SynchronizationContext.Current,除非它是null,在这种情况下它是TaskScheduler.Current。如果你在控制台应用程序中运行这个,那么上下文通常是线程池上下文,所以async方法将在线程池线程上继续执行。但是,如果在UI线程上执行相同的方法,那么上下文就是UI上下文,async方法将在UI线程中继续执行。

幕后C#编译器实际上将代码转换为状态机。它生成了更多的代码,这样每当等待任务或异步操作完成时,它都会在幕后从停止的地方继续执行。就你的问题而言,每次异步操作完成后,当你最初开始调用异步方法时,异步方法都会在调用线程上被回调。例如,它将在你启动的线程上执行你的代码。因此,异步操作将在Task线程上运行,然后结果将返回到你最初调用方法的线程上并继续执行。

Await将从Task或异步操作中获取值,并在返回执行时将其从任务中"取消装箱"。在这种情况下,它会自动将其放入int值中,因此无需存储Task<int>

您的代码在LongRunningTask()上有等待的问题,您很可能只想返回不带async的长任务方法,然后让MyMethod执行等待。

int value = await LongWaitingTask()

异步等待和生成的状态机

返回Taskvoidasync方法的要求。

可以更改它,这样当您从执行异步任务返回时,它将在使用Task.ConfigureAwait方法执行异步任务的线程上执行剩余代码。

这样想可能更容易:每当您有一个await时,编译器就会将您的方法拆分为2部分:一部分在await之前,另一部分在它之后。第二部分在第一部分成功完成后运行。

在您的代码中,第一种方法看起来大致相当于以下内容:

public async Task MyMethod()
{
    Task<int> longRunningTask = LongRunningOperation();
    MySynchronousMethod();
    longRunningTask.ContinueWith(t => part2(t.Result));
}
void part2(int result)
{
    Console.WriteLine(result);
}

几个重要注意事项:

  1. 它显然比这个复杂得多,因为它应该支持试试catch和其他。此外,它并没有真正使用任务
  2. CCD_ 49实际上并没有被直接使用。它使用任务的GetAwaiter()方法及其API,或使用此方法或扩展方法的任何其他类
  3. 如果一个方法中有多个等待,它将被多次拆分
  4. 如果MyMethod被拆分,等待MyMethod的人如何知道所有部分何时完成?当异步方法返回一个Task时,编译器会生成一个特殊的Task,它会用状态机跟踪所有内容