C#中async和await的定义及使用

3,820 阅读3分钟

前言:

C# 5 给我们带来了 async 异步方法,它的出现标志着【次世代】的来临——它可以将多个基于 Task 的操作结合到一起,使代码编写更加直观容易。但 async 也存在额外开销,Task 是引用类型,创建 Task 对象的时候会向堆区申请内存,即使 async 以同步的方式运行,也会有性能开销。到了C# 7,async 方法开始支持返回类Task (task-like)的类型,如 ValueTask ,来减少申请堆内存的次数或在某些情况下完全避免。今天我们主要来了解下如何在c#中借助Task来使用异步,task-like将在下一篇中介绍!

一、async和await的定义

async:作为一个关键字放到函数前面,被async标记不代表该函数就以异步的方式执行,它只是做个标记,让编译器在遇到该标记时对async方法做一些特殊的处理,要有await关键字才说明是异步方法。异步函数也就意味着该函数的执行不会阻塞后面代码的执行

await:意为等待,就是需要等待await后面的函数运行完并且有了返回结果之后,才继续执行下面的代码,这正是同步的效果,await作用就是将异步方法转同步。

  1. await关键字,只能放在async函数里面!!!
  2. async函数中可以没有await关键字,此时编译器将向我们显示警告缺少await,但不会显示任何错误。
  3. 一个async函数中可以有多个await
  4. async只需要放在函数的返回类型前的任意位置

二、异步方法的返回类型

异步函数的返回类型只能为: void、Task、Task。

Task: 代表一个返回值T类型的操作。

Task: 代表一个无返回值的操作。

void: 为了和传统的事件处理程序兼容而设计。

private async Task Test_one_async() //这是一个无返回值的异步方法
private async Task<string> Test_two_async() //这是一个返回值类类型是string的异步方法
private async void Test_two_async() //这是一个异步事件处理程序

三、异步方法的适用场景

一般都是比较费时的任务,也就是会阻塞主线程的一些操作,比如获取Http应答,写入文档,读取大文档,保存读取数据库等,将这些方法写入异步函数中,可以达到不阻塞主线程后续方法的目的。

包含异步方法的API有HttpClient, SyndicationClient, StorageFile, StreamWriter, StreamReader, XmlReader, MediaCapture, BitmapEncoder, BitmapDecoder 等

四、异步使用sample

  1. 在这个例子中,我们将采取两个不相互依赖的方法。Method 1和Method 2不相互依赖,我们是从主方法调用的。在这里,我们可以清楚地看到,方法1和方法2并不是在等待对方完成。
        static void Main(string[] args)
        {
            Console.WriteLine("主线程开始");
            Method1();
            Method2(); // 由于方法一是异步方法,所以方法二不会等到一执行完再执行
            Console.ReadKey();
        }

        public static async Task Method1()
        {
            await Task.Run(() =>
            {
                for (int i = 0; i < 100; i++)
                {
                    Console.WriteLine(" Method 1");
                }
            });
        }
        public static void Method2()
        {
            for (int i = 0; i < 25; i++)
            {
                Console.WriteLine(" Method 2");
            }
        } 
  1. 我们将创建一个新的方法,作为CallMethod,在这个方法中,我们将调用我们的所有方法,分别为Method 1、Method 2和Method 3。Method 3需要一个参数,即Method 1的返回类型。在这里,await关键字对于等待Method 1任务的完成起着至关重要的作用。
        static void Main(string[] args)
        {
            Console.WriteLine("主线程开始");
            callMethod();
            Console.ReadKey();
        }

        public static async void callMethod()
        {
            Task<int> task = Method1(); //立即执行方法一
            Method2(); // 方法二也立即执行,不用等方法一结束,两个方法互不影响,相互独立
            int count = await task; //等待方法一结束,才会执行方法三
            Method3(count);  
        }

        public static async Task<int> Method1()
        {
            int count = 0;
            await Task.Run(() =>
            {
                for (int i = 0; i < 100; i++)
                {
                    Console.WriteLine(" Method 1");
                    count += 1;
                }
            });
            return count;
        }
        public static void Method2()
        {
            for (int i = 0; i < 25; i++)
            {
                Console.WriteLine(" Method 2");
            }
        }
        public static void Method3(int count)
        {
            Console.WriteLine("Total count is " + count);
        }

明白上面的代码,我们就可以在C#代码中使用async 和await关键字来愉快的进行异步编程了。