异步在 C# 中如何工作?

Posted

技术标签:

【中文标题】异步在 C# 中如何工作?【英文标题】:How does async works in C#? 【发布时间】:2011-05-02 03:18:06 【问题描述】:

Microsoft 今天(2010 年 10 月 28 日)宣布了 Visual Studio Async CTP,它将 asyncawait 关键字引入 C#/VB 以用于异步方法执行。

首先我认为编译器会将关键字转换为线程的创建,但根据 white paper 和 Anders Hejlsberg 的 PDC presentation(在 31:00),异步操作完全发生在主线程上。

如何在同一个线程上并行执行一个操作?它在技术上是如何实现的?在 IL 中实际翻译的功能是什么?

【问题讨论】:

【参考方案1】:

它的工作方式类似于 C# 2.0 中的 yield return 关键字。

异步方法实际上不是普通的顺序方法。它被编译成具有某些状态的状态机(对象)(局部变量被转换为对象的字段)。两次使用await 之间的每个代码块都是状态机的一个“步骤”。

这意味着当方法启动时,它只是运行第一步,然后状态机返回并安排一些要完成的工作——当工作完成时,它将运行状态机的下一步。例如这段代码:

async Task Demo()  
  var v1 = foo();
  var v2 = await bar();
  more(v1, v2);

会被翻译成这样的:

class _Demo 
  int _v1, _v2;
  int _state = 0; 
  Task<int> _await1;
  public void Step() 
    switch(this._state) 
    case 0: 
      this._v1 = foo();
      this._await1 = bar();
      // When the async operation completes, it will call this method
      this._state = 1;
      op.SetContinuation(Step);
    case 1:
      this._v2 = this._await1.Result; // Get the result of the operation
      more(this._v1, this._v2);
  

重要的部分是它只是使用SetContinuation方法来指定当操作完成时,它应该再次调用Step方法(并且该方法知道它应该运行原始代码的第二位使用_state 字段)。您可以轻松想象SetContinuation 类似于btn.Click += Step,它将完全在单个线程上运行。

C# 中的异步编程模型非常接近 F# 异步工作流(实际上,除了一些技术细节之外,它本质上是相同的),使用 async 编写反应式单线程 GUI 应用程序是相当有趣的区域 - 至少我是这么认为的 - 参见例如 this article(也许我现在应该写一个 C# 版本:-))。

翻译类似于迭代器(和yield return),事实上,之前可以使用迭代器在 C# 中实现异步编程。我不久前写了an article about that - 我认为它仍然可以让您了解翻译的工作原理。

【讨论】:

【参考方案2】:

如何在同一个线程上并行执行一个操作?

你不能。 异步不是“并行”或“并发”。异步可能是通过并行实现的,也可能不是。它可以通过将工作分成小块来实现,将每个工作块放在一个队列中,然后在线程碰巧没有做任何其他事情时执行每个工作块。

我的博客上有一系列关于这些东西如何工作的文章;与这个问题直接相关的问题可能会在下周星期四出现。观看

Link

了解详情。

【讨论】:

【参考方案3】:

据我了解,asyncawait 关键字的作用是,每次 async 方法使用 await 关键字时,编译器会将方法的其余部分转换为预定的延续当异步操作完成时。这允许async 方法立即返回调用者并在异步部分完成后恢复工作。

根据可用的文件,它有很多细节,但除非我弄错了,否则这就是它的要点。

在我看来,异步方法的目的不是并行运行大量代码,而是将异步方法分成许多可以根据需要调用的小块。关键是编译器将使用任务/延续来处理所有复杂的回调连接。这不仅降低了复杂性,而且允许或多或少地像传统同步代码一样编写异步方法。

【讨论】:

在没有单独线程的情况下如何调度?延续是 CLR 中允许一些轻量级调度的特定概念吗? @0xA3:我相信论文说异步方法不在它自己的线程上运行。 IE。就像 TPL 一样,它将根据情况混合当前线程和线程池线程。

以上是关于异步在 C# 中如何工作?的主要内容,如果未能解决你的问题,请参考以下文章

@foreach 循环中的多种形式。如何使用 javascript 异步提交一个。 C# 核心剃刀

如何在 C# 中启动一个与非异步函数异步的函数? [复制]

GetSchema 方法可以异步工作吗?

C#:异步NamedPipeServerStream理解

c#中的Task异步编程

如何在 C# 中创建异步方法?