• C#多线程编程(2) async,await基本用法


    上一章我简单介绍了异步编程的基本方法,推荐使用的方式是Task。Task是对线程池的封装,并且可以对Task使用async和await关键字。这两个关键字的使用非常简单,那么这两个关键字究竟起什么作用?工作原理是怎样的?本文就来简单解释。

    本系列是我读《CLR via C#》的总结,但是书中关于async和await关键字的讲解不是很多。其中28.3小节通过简单例子以及IL反编译的方式,讲解了编译器如何将异步函数编译成状态机,虽然反编译出的代码中作者添加了大量的注释,无奈本人能力有限,很多底层代码不能很好的理解,因此我通过小例子的方式反复的尝试,加上书中的讲解,我想我已经基本掌握了async和await的使用方式和基本原理。若您有兴趣了解底层代码,可以找来《CLR via C#》的28.2和28.3章节来读一读。

    上一章节已经讲了,当你想要利用另一个线程来执行一段程序,推荐的方式是Task,我们看个例子:

     public async void RunAsync(){
                var t = await Task.Run(() =>
                {
    //模拟其他操作 Thread.Sleep(
    2000); return "task finished"; }); Console.WriteLine(t); }

    运行代码,可以看到程序在大约2秒后,控制台会打印出"task finished",且在这过程中,主线程没有被Sleep(阻塞),RunAsync后面的方法可以继续执行,如

    1 static void Main(string[] args) {
    2             RunAsync();
    3             Console.WriteLine("123");
    4             Console.Read();
    5 }

    控制台会先显示123,过大约2秒后,显示task finished。程序在执行RunAsync()后,跳过了该方法,直接执行了Console.WriteLine("123")。可以看到RunAsync方法的签名中添加了async关键字,在Task.Run()前面添加了await关键字,这两个关键字的作用是表示RunAsync方法在执行到await关键字后,会将该方法的其余部分封装成一个委托,该委托会在Task.Run()返回的task执行完成后,执行该委托(具体编译器如何将该方法转换成状态机,并在任务结束后,再继续执行该委托,可以看《CLR via C#》的28.3章节)。

     1  public async void RunAsync(){
     2             //其他操作
     3             //当遇到await,会将后面的程序封装成一个委托
     4             var t = await Task.Run(() =>
     5             {
     6                 Thread.Sleep(2000);
     7                 return "task finished";
     8             });
     9             //这里往后的代码被封装成委托,当t.IsComplete后,才会被执行
    10             //其他操作
    11             Console.WriteLine(t);
    12         }

    关键字async表明该方法是一个异步方法,await关键字只允许在标有async的方法中使用。当异步方法具有返回值时,调用该异步方法的函数也要添加async关键字,并在调用方法处添加await,不然会造成异步失效。

     1  static void Main(string[] args){
     2             Console.WriteLine(RunAsync().Result);
     3             Console.WriteLine("Async Run");
     4             Console.Read();
     5         }
     6         public static async Task<string> RunAsync(){
     7             return await Task.Run(() =>
     8             {
     9                 Thread.Sleep(2000);
    10                 return "task finished";
    11             });
    12         }

    运行,程序在等待大约2秒后,显示task finished,然后再显示Async Run,这是因为虽然RunAsync方法异步返回,但是主线程一直在等待RunAsync的结果,除此之外什么也不干,这样当然是不好。

     1 static void Main(string[] args){
     2    TestAsync();
     3    Console.WriteLine("Async Run");
     4    Console.Read();
     5 }
     6 public static async Task<string> RunAsync(){
     7     return await Task.Run(() =>
     8     {
     9         Thread.Sleep(2000);
    10         return "task finished";
    11     });
    12 }
    13 public static async void TestAsync(){
    14     Console.WriteLine(await RunAsync());
    15 }

    Main函数无法添加async关键字,因此我用TestAsync方法包装了一下,可以看到添加了await 和async关键字后,程序会先出现Async Run,然后是task finished。即在异步函数的调用中,若函数包含返回值,则应通过添加await的方式调用异步函数,这样可以做到接着异步,而不是半途而废的异步。

    在循环中也可以添加await关键字

     1 static void Main(string[] args)
     2 {
     3     TestAsync();
     4     Console.WriteLine("Async Run");
     5     Console.Read();
     6 }
     7 public static async Task<string> RunAsync()
     8 {
     9     return await Task.Run(() =>
    10     {
    11         Thread.Sleep(2000);
    12         return "task finished";
    13     });
    14 }
    15 public static async void TestAsync()
    16 {
    17     for (int i = 0; i < 4; i++)
    18     {
    19         Console.WriteLine(await RunAsync() + i);
    20     }
    21 }

    上述例子会以你希望的那样,先显示Async Run,然后依次打印task finished0 - task finished4,且每次打印间歇大约2秒。

    以上就是Task与 async 和 await 关键字的使用以及基本原理。使用await关键字,该关键字之后的逻辑都会被封装到一个委托,等到任务执行结束后,再调用当前线程继续执行该委托。那么能够调用当前线程,也应该有方法当任务执行结束后,继续调用线程池来执行方法。该部分C#多线程编程(3)会有讲解。欢迎有问题的小伙伴和我在评论区交流。

  • 相关阅读:
    安装python包
    在RHEL5.4上升级Python
    IronPython开发Windows Form程序总结
    Windows下手动配置Oracle Client的要点
    dreampie一个很不错的python命令行交互工具
    Eclipse插件汇总
    pyDbRowFactory Python版Db Row Factory
    如何让Jython自动加载一个Jar包
    跨计算机执行的几个方法
    Python 版 Instance Activator
  • 原文地址:https://www.cnblogs.com/jazzpop/p/8521487.html
Copyright © 2020-2023  润新知