Async和Await的工作原理

时间:2014-03-12 10:52:38

标签: c# async-await

我试图了解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已经完成执行并且结果可用。当到达等待longRunningOperation时,编译器知道它有结果并将继续在同一个线程上执行代码。 (在这种情况下打印结果到控制台)。

第1点对我来说并不清楚,就像#34; if the longRunningOperation hasn't finished and is still running, MyMethod() will return to its calling method&#34;

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

4 个答案:

答案 0 :(得分:24)

我已经按照以下方式接受了教育,我发现这是一个非常明确和简洁的解释:

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

调用Method时,会执行其内容(code;行),直到await something;。此时,something;被触发,方法结束就像return;那样。

something;做了它需要的东西,然后返回。

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

以更加示意性的方式,这里发生了什么:

  1. 调用方法
  2. 执行
  3. code;
  4. something;已执行,流程返回到Method被调用的位置
  5. 继续执行Method调用
  6. 之后的内容
  7. something;返回时,流程返回Method
  8. moreCode;已执行且Method本身已结束(是的,可能还有其他内容await - 依赖于此类似等等)

答案 1 :(得分:22)

我的博客上有async intro,您可能会觉得有帮助。

此代码:

int result = await LongRunningOperation();

与此代码基本相同:

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

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

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

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

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

这&#34; context&#34;是SynchronizationContext.Current,除非它是null,在这种情况下它是TaskScheduler.Current。如果您在Console应用程序中运行此操作,则上下文通常是线程池上下文,因此async方法将在线程池线程上继续执行。但是,如果在UI线程上执行相同的方法,则上下文是UI上下文,async方法将在UI线程上继续执行。

答案 2 :(得分:7)

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

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

你的代码有问题,它等待在LongRunningTask()上,你很可能只想在没有异步的情况下返回长任务方法,然后让你的MyMethod执行await。

int value = await LongWaitingTask()

http://www.codeproject.com/Articles/535635/Async-Await-and-the-Generated-StateMachine

异步方法的要求是返回Task或void。

可以更改它,以便当您从执行异步任务返回时,它将执行线程上的剩余代码,使用http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.configureawait(v=vs.110).aspx执行异步任务

答案 3 :(得分:0)

这样思考可能会更容易: 只要有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); } 之后的另一部分。 第二部分在第一个部分成功完成之后运行。

在您的代码中,第一个方法看起来大致与此等效:

Task

一些重要说明:

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