一文带你搞懂C# 异步编程(async/await)底层原理

基于async/await的异步编程

我们先看一下未使用异步时的一段代码,这段代码的作用是从源流拷贝字节到目标流:

如果流里面的数据比较多,未使用异步时,调用线程会被阻塞较长的时间。当调用端是桌面程序时,就会出现我们常见的界面假死情况。

 1 // Synchronously copy all data from source to destination.
 2 public void CopyStreamToStream(Stream source, Stream destination)
 3 {
 4     var buffer = new byte[0x1000];
 5     int numRead;
 6     while ((numRead = source.Read(buffer, 0, buffer.Length)) != 0)
 7     {
 8         destination.Write(buffer, 0, numRead);
 9     }
10 }

demo

 

然后我们使用async/await关键字,并把方法修改为异步方法版本,此时调用线程就不会被阻塞。

 1 // Asynchronously copy all data from source to destination.
 2 public async Task CopyStreamToStreamAsync(Stream source, Stream destination)
 3 {
 4     var buffer = new byte[0x1000];
 5     int numRead;
 6     while ((numRead = await source.ReadAsync(buffer, 0, buffer.Length)) != 0)
 7     {
 8         await destination.WriteAsync(buffer, 0, numRead);
 9     }
10 }

demo

 

async/await关键字的内部到底做了什么?让我们接着往下看。

 

我为什么会有这个疑问?

因为平常我会喜欢反编译一些.NET程序集,如果在代码中使用了异步编程,也就是使用了async和await关键字。反编译出来的代码就会显得比较复杂。

所以我就想去详细了解一下基于async/await的异步编程底层原理到底是什么,也就有了这篇文章。

 

我们可以先验证一下这个问题:

创建一个WPF工程

MainWindow.xaml

 1 <Window x:Class="AsyncDemo.MainWindow"
 2         Title="MainWindow" Height="450" Width="800">
 3     <Grid>
 4         <Grid.RowDefinitions>
 5             <RowDefinition/>
 6             <RowDefinition Height="auto"/>
 7         </Grid.RowDefinitions>
 8 
 9         <RichTextBox Name="rtbox" VerticalScrollBarVisibility="Auto"></RichTextBox>
10 
11         <Button Grid.Row="1" HorizontalAlignment="Center" Width="88" Height="28" VerticalAlignment="Center" Content="Ok" Click="Button_Click" Margin="15"></Button>
12     </Grid>
13 </Window>

 

我们先用同步的方式实现一下:

MainWindow.xaml.cs

 1  public partial class MainWindow : Window
 2  {
 3      public MainWindow()
 4      {
 5          InitializeComponent();
 6      }
 7 
 8      private void Button_Click(object sender, RoutedEventArgs e)
 9      {
10          GetHtmlSource("http://www.baidu.com");
11      }
12 
13      private void GetHtmlSource(string url)
14      {
15          WebClient webClient = new WebClient();
16          var html = webClient.DownloadString(url);
17          this.rtbox.Document = new FlowDocument(new Paragraph(new Run(html)));
18      }
19  }

 

这个时候我们用反编译工具反编译一下生成的exe,可以看到整体差异不大。

image

 

然后我们使用Task将同步任务进行包装

1    private async void Button_Click(object sender, RoutedEventArgs e)
2    {
3        //同步
4        //GetHtmlSource("http://www.baidu.com");
5 
6        //异步
7        await Task.Run(() => { GetHtmlSource("http://www.baidu.com"); });
8    }

 

此时我们再看一下反编译工具里反编译出来的代码,就会发现有较大的变化 ,编译器生成了一堆奇奇怪怪的代码,如下所示:

 1 public class MainWindow : Window, IComponentConnector
 2 {
 3     // Fields
 4     internal RichTextBox rtbox;
 5     private bool _contentLoaded;
 6 
 7     // Methods
 8     public MainWindow()
 9     {
10         this.InitializeComponent();
11     }
12 
13     [AsyncStateMachine(typeof(<Button_Click>d__1)), DebuggerStepThrough]
14     private void Button_Click(object sender, RoutedEventArgs e)
15     {
16         <Button_Click>d__1 stateMachine = new <Button_Click>d__1 {
17             <>t__builder = AsyncVoidMethodBuilder.Create(),
18             <>4__this = this,
19             sender = sender,
20             e = e,
21             <>1__state = -1
22         };
23         stateMachine.<>t__builder.Start<<Button_Click>d__1>(ref stateMachine);
24     }
25 
26     private void GetHtmlSource(string url)
27     {
28         string html = new WebClient().DownloadString(url);
29         if (Application.Current.Dispatcher.CheckAccess())
30         {
31             this.rtbox.Document = new FlowDocument(new Paragraph(new Run(html)));
32         }
33         else
34         {
35             Application.Current.Dispatcher.Invoke(() => this.rtbox.Document = new FlowDocument(new Paragraph(new Run(html))));
36         }
37     }
38 
39     // Nested Types
40     [CompilerGenerated]
41     private sealed class <Button_Click>d__1 : IAsyncStateMachine
42     {
43         // Fields
44         public int <>1__state;
45         public AsyncVoidMethodBuilder <>t__builder;
46         public object sender;
47         public RoutedEventArgs e;
48         public MainWindow <>4__this;
49         private TaskAwaiter <>u__1;
50 
51         // Methods
52         private void MoveNext()
53         {
54             int num = this.<>1__state;
55             try
56             {
57                 TaskAwaiter awaiter;
58                 if (num != 0)
59                 {
60                     awaiter = Task.Run(new Action(this.<>4__this.<Button_Click>b__1_0)).GetAwaiter();
61                     if (!awaiter.IsCompleted)
62                     {
63                         this.<>1__state = num = 0;
64                         this.<>u__1 = awaiter;
65                         MainWindow.<Button_Click>d__1 stateMachine = this;
66                         this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, MainWindow.<Button_Click>d__1>(ref awaiter, ref stateMachine);
67                         return;
68                     }
69                 }
70                 else
71                 {
72                     awaiter = this.<>u__1;
73                     this.<>u__1 = new TaskAwaiter();
74                     this.<>1__state = num = -1;
75                 }
76                 awaiter.GetResult();
77             }
78             catch (Exception exception)
79             {
80                 this.<>1__state = -2;
81                 this.<>t__builder.SetException(exception);
82                 return;
83             }
84             this.<>1__state = -2;
85             this.<>t__builder.SetResult();
86         }
87 
88         [DebuggerHidden]
89         private void SetStateMachine(IAsyncStateMachine stateMachine)
90         {
91         }
92     }
93 }
94 
95  

 

这些代码都是编译器帮我们生成的。微软简化了异步编程,让开发者使用async/await关键字就可以轻松的实现异步编程,但是实际上它内部是做了很多工作的。

 

下面我们来深入理解一下,基于async/await的异步编程到底是怎么实现的。

 

待更新

 

 

参考资料:

https://devblogs.microsoft.com/dotnet/how-async-await-really-works/

posted @ 2026-05-20 23:21  zhaotianff  阅读(33)  评论(0)    收藏  举报