• 多线程编程学习笔记——async和await(一)


    接上文 多线程编程学习笔记——任务并行库(一)

    接上文 多线程编程学习笔记——任务并行库(二)

     接上文 多线程编程学习笔记——任务并行库(三)

      接上文 多线程编程学习笔记——任务并行库(四)

     

            通过前面的文章,已经学习了怎么使用线程,怎么使用线程同步,怎么使用线程池,怎么使用任务并行库。尽管通过上面的学习,对于线程的使用越来越简单。有没有更简单的方法呢。

           C# 5.0之后,微软在c#语言中添加了两个关键字async与await,这是在TPL上面的更高一级的抽象,真正简化了异步编程的编程方式,从而有助于我们编写出真正健壮少bug的异步应用程序。下面我先来看一个最简单的示例。

    async Task<string> AsyncHello()
    {
    
    await Task.Delay(TimeSpan.FromSeconds(2));
    Return “ Hello world”;
    }

             使用async标记异步函数,建议返回async Task<T>。

             Await只能使用在有async标志的方法内部。在async标记的方法内部最少要有一个await,当然,如果一个也没有,编译也不会报错,但是会有编译警告。如下图。

         

           上面的代码在执行完await调用的代码之行后该方法会直接返回。如果同步执行,执行线程会阻塞2秒之后返回结果,本示例里在执行完await操作后,立即将工作线程放回线程池中,我们会异步等待。2秒后,我们会从线程池中取得工作线程并继续运行其中剩余的异步方法。这就允许我们在等待的2秒的时间里可以重用线程池中的线程,这对提高应用程序的可伸缩性非常重要。通过使用async与await我们拥有了线性的程序控制流程,但是执行过程却是异步的。

     

    一、   使用await获取异步操作结果

            本示例是学习await如何获取异步操作结果。同时会与TPL进行比较。

     1.示例代码如下。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Threading; 
    
    namespace ThreadAsyncDemo
    {
    
        class Program
        {
            static void Main(string[] args)
            {
    
                Task t = AsyncWithTPL();
                t.Wait();
                t = AsyncWithAwait();
                t.Wait();
                Console.Read();
            }
    
     
    
            static Task AsyncWithTPL()
            {
                Task<string> task1 = GetInfoAsync("Task 1");
    
                Task task2 = task1.ContinueWith(task =>
                  Console.WriteLine(task1.Result), TaskContinuationOptions.NotOnFaulted);
                Task task3 = task1.ContinueWith(task =>
                  Console.WriteLine(task1.Exception.InnerException), TaskContinuationOptions.OnlyOnFaulted);
                return Task.WhenAny(task2, task1);
    
            }
    
            async static Task AsyncWithAwait()
            {
                try
                {
                    string result = await GetInfoAsync("Task 4");
                    Console.WriteLine(result);
                }
    
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
    
                }
            } 
    
            async static Task<string> GetInfoAsync(string name)
            {
    
                await Task.Delay(TimeSpan.FromSeconds(2));
                //throw  new Exception("抛出异常信息!"); 
    
                return string.Format(" Task {0} 正在运行在线程 ID={1}上。这个工作线程是否是线程池中的线程:{2}", name, 
    Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); } } }

     2.程序运行的结果如下图。

     

             程序同时运行了两个异步操作。其中一个是标准的TPL代码,另一个使用了async与await两个关键字。AsyncWithTPL启动了一个任务,运行两秒之后返回关于工作线程信息的字符串。然后我们定义了一个后续操作,用于在异步操作完成后打印出操作结果,还有另一个后续操作,用于万一有错误时,打印出异常信息。最终返回了一个代表其中一个后续操作任务的任务,并等等其在主函数中完成。

            在asyncWithAwait方法中,我们对任务使用await并得到了相同 的结果。这和编写普通的同步代码的风格一样,即我们获取了任务的结果,打印了出来,如果任务完成时带有错误则捕获异常。关键不同的是这实际上是一个异步操作。使用await后,c#立即创建了一个任务,其中一个有后续操作任务,包含了await操作符后面的所有剩余代码。这个新任务也处理了异常。然后这个任务返回 到主方法并等待共完成 。

           因此可以看出程序中的两段代码在概念上是相同的,使用await由编译 器隐式地处理了异步代码。

      3. 我们把上面注释的抛出异常的代码,取消注释,然后运行程序。得到如下图的结果。

     

                注:在gui与asp.net之类的环境 中不推荐 使用task.wait和taskResult方法同,因为如果代码写的不好,很容易导致死锁。

    二、   在Lambda表达式中使用await操作符

             本示例学习如何在lambda表达式中使用await。将学习如何编写一个使用了await的匿名方法,并且获取异步执行该方法的结果。

     1.示例代码如下。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Threading; 
    
    namespace ThreadAsyncDemo
    {
    
        class Program
        {
    
            static void Main(string[] args)
            {
    
                Task t = AsyncProcess();
                t.Wait();       
                Console.Read();
            } 
    
            async static Task AsyncProcess()
            {
    
                Func<String, Task<string>> asyncLambda = async name =>
                {
    
                    await Task.Delay(TimeSpan.FromSeconds(2));
                    return string.Format(" Task {0} 正在运行在线程 ID={1}上。这个工作线程是线程池的线程:{2}" ,name, 
    Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); };
    string result = await asyncLambda("async lambda"); Console.WriteLine(result); } } }

    2.程序运行结果,如下图。

     

            首先不能在main方法中使用async,我们将异步函数移到了asyncProcess中,然后使用async关键字声明了一个lambda表达式。由于 任何lambda表达式的类型都不能通过lambda自身来推断,所以不得不显示地指定类型为一字符串,并返回一个Task<string>对象 。

            然后,我们定义 了lambda表达式体,这个方法虽然定义返回的是一个Task<string>对象 ,但实际上返回的是字符串,却没有编译错误。这是因为c#编译器自动 产生了一个任务并返回给我们。

           最后一步就是打印出lambda 表达式执行后的结果。

     

  • 相关阅读:
    系统集成项目管理工程师高频考点(第一章)
    2、无重复字符的最长子串
    1、爬楼梯
    webpack起步
    Centos7安装nginx
    Centos7安装nacos
    Centos7安装java和maven
    centos7安装fastDFS
    aop中获取请求消息和属性
    数据库面对高并发的思路
  • 原文地址:https://www.cnblogs.com/chillsrc/p/8057913.html
Copyright © 2020-2023  润新知