繁体   English   中英

异步和等待的工作原理

[英]How Async and Await works

我试图了解 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 已经完成执行并且结果可用。 当到达 await 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;一样结束return; 在那里。

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

something; 返回,执行返回到Method并从await开始,执行moreCode;

以更加示意性的方式,以下是发生的情况:

  1. 方法被调用
  2. code; 被执行
  3. something; 被执行,流程回到调用Method的点
  4. Method调用之后继续执行
  5. something; 返回,流在Method内部返回
  6. moreCode; 被执行并且Method本身结束(是的,可能还有其他东西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. 它显然比这复杂得多,因为它应该支持 try-catch 和其他。 此外,它并没有真正使用任务
  2. 实际上并没有直接使用Task 它使用任务的GetAwaiter()方法及其 API,或任何其他具有此方法或扩展方法的类。
  3. 如果一个方法中有多个等待,它会被多次拆分。
  4. 如果MyMethod被拆分,等待MyMethod人如何知道所有部分何时完成? 当您的 async 方法返回Task ,编译器会生成一个特殊的Task ,它使用状态机跟踪所有内容。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM