一文带你搞懂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 }

然后我们使用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 }

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,可以看到整体差异不大。

然后我们使用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/

浙公网安备 33010602011771号