欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-03-29 11:33:57
通过前面的文章,已经学习了怎么使用线程,怎么使用线程同步,怎么使用线程池,怎么使用任务并行库。尽管通过上面的学习,对于线程的使用越来越简单。有没有更简单的方法呢。 C# 5.0之后,微软在c#语言中添加了两个关键字async与await,这是在TPL上面的更高一级的抽象,真正简化了异... ......

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

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

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

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

 

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

       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,当然,如果一个也没有,编译也不会报错,但是会有编译警告。如下图。

      多线程编程学习笔记——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.程序运行的结果如下图。

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

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

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

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

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

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

            注:在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.程序运行结果,如下图。

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

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

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

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