一篇文章弄懂C#中的async和await

 更新时间:2021年7月1日 15:00  点击:2206

前言

本文介绍async/Task。在学习该知识点过程中,一定要按照每一个示例,去写代码、执行、输出结果,自己尝试分析思路。

async

微软文档:使用 async 修饰符可将方法、lambda 表达式或匿名方法指定为异步。

使用 async 修饰的方法,称为异步方法。

例如:

为了命名规范,使用 async 修饰的方法,需要在方法名称后面加上 Async 。

public async Task<int> TestAsync()  
{  
    // . . . .  
}
Lambda :


        static void Main()
        {
            Thread thread = new Thread(async () =>
            {
                await Task.Delay(0);
            });
        }
        public static async Task<int> TestAsync() => 666;

await

微软文档:await 运算符暂停对其所属的 async 方法的求值,直到其操作数表示的异步操作完成。

异步操作完成后,await 运算符将返回操作的结果(如果有)。

好的,到此为止,async 和 await ,在官方文档中的说明,就这么多。

从以往知识推导

这里我们不参考文档和书籍的资料,不要看文档和书籍中的示例,我们要一步步来从任务(Task)中的同步异步开始,慢慢摸索。去分析 async 和 await 两个关键字给我们的异步编程带来了什么样的便利。

创建异步任务

场景:周六日放假了,可以打王者(一种游戏),但是昨天的衣服还没有洗;于是用洗衣机洗衣服,清洗期间,开一局王者(一种游戏)。

我们可以编写一个方法如下:

        static void Main()
        {
            Console.WriteLine("准备洗衣服");

            // 创建一个洗衣服的任务
            Task<int> task = new Task<int>(() =>
            {
                // 模拟洗衣服的时间
                int time = new Random().Next(2, 6);
                Thread.Sleep(TimeSpan.FromSeconds(time));
                return time;
            });

            Console.WriteLine("开始洗衣服");

            // 让洗衣机洗衣服
            task.Start();

            Console.WriteLine("我去打王者,让洗衣机洗衣服");
            // 打王者
            Thread.Sleep(TimeSpan.FromSeconds(4));
            Console.WriteLine("打完王者了,衣服洗完了嘛?");

            Console.WriteLine(task.IsCompleted);
            if (task.IsCompleted)
                Console.WriteLine("洗衣服花的时间:" + task.Result);
            else
            {
                Console.WriteLine("在等洗衣机洗完衣服");
                task.Wait();
                Console.WriteLine("洗衣服花的时间:" + task.Result);
            }
            Console.WriteLine("洗完了,捞出衣服,晒衣服,继续打王者去");
        }

创建异步任务并返回Task

上面的示例,虽然说,异步完成了一个任务,但是这样,将代码都放到 Main ,可读性十分差,还要其它什么规范之类的,不允许我们写这样的垃圾代码。于是我们将洗衣服这个任务,封装到一个方法中,然后返回 Task 即可。

在 Program 类中,加入如下一个方法,这个方法用于执行异步任务,并且返回 Task 对象。

        /// <summary>
        /// 执行一个任务
        /// </summary>
        /// <returns></returns>
        public static Task<int> TestAsync()
        {
            Task<int> task = new Task<int>(() =>
            {
                // 模拟洗衣服的时间
                int time = new Random().Next(2, 6);
                Thread.Sleep(TimeSpan.FromSeconds(time));
                return time;
            });
            task.Start();
            return task;
        }

Main 方法中,改成

        static void Main()
        {
            Console.WriteLine("准备洗衣服");

            // 创建一个洗衣服的任务
            Task<int> task = TestAsync();
            ... ...

但是,两者差别还是不大。

异步改同步

我们创建了异步方法,去执行一个洗衣服的任务;当打完游戏后,需要检查任务是否完成,然后才能进行下一步操作,这时候就出现了 同步。为了保持同步和获得执行结果,我们使用了 .Wait() 、.Result 。

这里我们尝试将上面的操作转为同步,并且获得执行结果。

    class Program
    {
        static void Main()
        {
            int time = Test();
            // ... ...
        }

        /// <summary>
        /// 执行一个任务
        /// </summary>
        /// <returns></returns>
        public static int Test()
        {
            Task<int> task = new Task<int>(() =>
            {
                // 模拟洗衣服的时间
                int time = new Random().Next(2, 6);
                Thread.Sleep(TimeSpan.FromSeconds(time));
                return time;
            });
            task.Start();
            return task.Result;
        }
    }

说说 await Task

Task 和 Task<TResult> ,前者是一个没有返回结果的任务,后者是有返回结果的任务。前面的文章中已经使用过大量的示例,这里我们使用 await ,去完成一些完全相同的功能。

Task:

        public static void T1()
        {
            Task task = new Task(() => { });
            task.Wait();
        }

        public static async void T2()
        {
            Task task = new Task(() => {  });
            await task;
        }

说明,await 可以让程序等待任务完成。

Task<TResult>:

       public void T3()
       {
           // 获取 Task 任务对象,后面的逻辑过程可以弄成异步
           Task<int> task = TestAsync();

           // 任务是异步在执行,我不理会他
           // 这里可以处理其它事情,处理完毕后,再获取执行结果
           // 这就是异步

           Console.WriteLine(task.Result);
       }

        public async void T4()
        {
            // 使用 await 关键字,代表等待执行完成,同步
            int time = await TestAsync();
            Console.WriteLine(time);
        }

说明:await 可以让程序等待任务执行完成,并且获得执行结果。

看到没有。。。await 关键字,作用是让你等,是同步的,压根不是直接让你的任务变成异步后台执行的。

那为啥提到 async 、await,都是说跟异步有关?不急,后面解释。

说说 async Task<TResult>

async Task<TResult> 修饰一个方法,那么这个方法要返回 await Task<TResult> 的结果。

两种同步方式示例对比:

        public static int Test()
        {
            Task<int> task = new Task<int>(() =>
            {
                // 模拟洗衣服的时间
                int time = new Random().Next(2, 6);
                Thread.Sleep(TimeSpan.FromSeconds(time));
                return time;
            });
            task.Start();
            return task.Result;
        }

        public static async Task<int> TestAsync()
        {
            Task<int> task = new Task<int>(() =>
            {
                // 模拟洗衣服的时间
                int time = new Random().Next(2, 6);
                Thread.Sleep(TimeSpan.FromSeconds(time));
                return time;
            });
            task.Start();
            int time = await task;
            return time;
        }

同步异步?

问:async 和 await 不是跟异步方法有关嘛,为啥前面的示例使用了 await ,全部变成同步了?

问:使用 async 和 await 的方法,执行过程到底是同步还是异步?

答:同步异步都行,要同步还是异步,全掌握在你的手上。

  • 你使用 await 去调用一个异步方法,其执行过程就是同步。
  • 你获取异步方法返回的 Task,就是异步。

最近笔者收到一些提问,有些读者,使用 async 和 await 去编写业务,想着是异步,可以提升性能,实际结果还是同步,性能一点没有提升。通过下面的示例,你会马上理解应该怎么用。

首先,在不使用 async 和 await 关键字的情况下,我们来编写两个方法,分别实现同步和异步的功能,两个方法执行的结果是一致的。

        /// <summary>
        /// 同步
        /// </summary>
        /// <returns></returns>
        public static int Test()
        {
            Task<int> task = new Task<int>(() =>
            {
                return 666;
            });
            task.Start();
            return task.Result;
        }
        
        /// <summary>
        /// 异步
        /// </summary>
        /// <returns></returns>
        public static Task<int> TestAsync()
        {
            Task<int> task = new Task<int>(() =>
            {
                return 666;
            });
            task.Start();
            return task;
        }

能不能将两个方法合并在一起呢?想同步就同步,想异步就异步,这样就不需要写两个方法了!

是可以的!通过 async 和 await 关键字,可以轻松实现!

合并后,代码如下:

        /// <summary>
        /// 可异步可同步
        /// </summary>
        /// <returns></returns>
        public static async Task<int> TestAsync()
        {
            Task<int> task = new Task<int>(() =>
            {
                return 666;
            });
            task.Start();
            return await task;
        }

合并后,我们又应该怎么在调用的时候,实现同步和异步呢?

笔者这里给出两个示例:

        // await 使得任务同步
        public async void T1()
        {
            // 使用 await 关键字,代表等待执行完成,同步
            int time = await TestAsync();
            Console.WriteLine(time);
        }

        // 直接获得返回的 Task,实现异步
        public void T2()
        {
            // 获取 Task 任务对象,后面的逻辑过程可以弄成异步
            Task<int> task = TestAsync();

            // 任务是异步在执行,我不理会他
            // 这里可以处理其它事情,处理完毕后,再获取执行结果
            // 这就是异步

            Console.WriteLine(task.Result);
        }

至此,理解为什么使用了 async 和 await,执行时还是同步了吧?

Task封装异步任务

前面,我们都是使用了 new Task() 来创建任务,而且微软官网大多使用 Task.Run() 来编写 async 和 await 的示例。

因此,我们可以修改前面的异步任务,改成:

        /// <summary>
        /// 可异步可同步
        /// </summary>
        /// <returns></returns>
        public static async Task<int> TestAsync()
        {
            return await Task.Run<int>(() =>
            {
                return 666;
            });
        }

关于跳到 await 变异步

在百度学习异步的时候,往往会有作者说,进入异步方法后,同步执行代码,碰到 await 后就是异步执行。

当然还有多种说法。

我们已经学习了这么多的任务(Task)知识,这一点十分容易解释。

因为使用了 async 和 await 关键字,代码最深处,必定会出现 Task 这个东西,Task 这个东西本来就是异步。碰到 await 出现异步,不是因为 await 的作用,而是因为最底层有个 Task。

为什么出现一层层的 await

这是相对于提供服务者来说。因为我要提供接口给你使用,因此底层出现 async、await 后,我会继续保留方法是异步的(async),然后继续封装,这样就有多层的调用结构,例如上一小节的图。

但是如果来到了调用者这里,就不应该还是使用 async 、await 去编写方法,而是应该按照实际情况同步或异步。

总结

到此这篇C#中async和await的文章就介绍到这了,更多相关C#中async和await内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!

[!--infotagslink--]

相关文章

  • 浅谈C# 字段和属性

    这篇文章主要介绍了C# 字段和属性的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下...2020-11-03
  • C#实现简单的登录界面

    我们在使用C#做项目的时候,基本上都需要制作登录界面,那么今天我们就来一步步看看,如果简单的实现登录界面呢,本文给出2个例子,由简入难,希望大家能够喜欢。...2020-06-25
  • C#中截取字符串的的基本方法详解

    这篇文章主要介绍了C#中截取字符串的的基本方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-11-03
  • C#实现简单的Http请求实例

    这篇文章主要介绍了C#实现简单的Http请求的方法,以实例形式较为详细的分析了C#实现Http请求的具体方法,需要的朋友可以参考下...2020-06-25
  • C#连接SQL数据库和查询数据功能的操作技巧

    本文给大家分享C#连接SQL数据库和查询数据功能的操作技巧,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友参考下吧...2021-05-17
  • C#中new的几种用法详解

    本文主要介绍了C#中new的几种用法,具有很好的参考价值,下面跟着小编一起来看下吧...2020-06-25
  • 使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)

    这篇文章主要介绍了使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • C#开发Windows窗体应用程序的简单操作步骤

    这篇文章主要介绍了C#开发Windows窗体应用程序的简单操作步骤,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-12
  • C#从数据库读取图片并保存的两种方法

    这篇文章主要介绍了C#从数据库读取图片并保存的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2021-01-16
  • C#和JavaScript实现交互的方法

    最近做一个小项目不可避免的需要前端脚本与后台进行交互。由于是在asp.net中实现,故问题演化成asp.net中jiavascript与后台c#如何进行交互。...2020-06-25
  • C++调用C#的DLL程序实现方法

    本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
  • 轻松学习C#的基础入门

    轻松学习C#的基础入门,了解C#最基本的知识点,C#是一种简洁的,类型安全的一种完全面向对象的开发语言,是Microsoft专门基于.NET Framework平台开发的而量身定做的高级程序设计语言,需要的朋友可以参考下...2020-06-25
  • C#变量命名规则小结

    本文主要介绍了C#变量命名规则小结,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-09
  • C#绘制曲线图的方法

    这篇文章主要介绍了C#绘制曲线图的方法,以完整实例形式较为详细的分析了C#进行曲线绘制的具体步骤与相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • C# 中如何取绝对值函数

    本文主要介绍了C# 中取绝对值的函数。具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25
  • c#自带缓存使用方法 c#移除清理缓存

    这篇文章主要介绍了c#自带缓存使用方法,包括获取数据缓存、设置数据缓存、移除指定数据缓存等方法,需要的朋友可以参考下...2020-06-25
  • c#中(&&,||)与(&,|)的区别详解

    这篇文章主要介绍了c#中(&&,||)与(&,|)的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
  • C#学习笔记- 随机函数Random()的用法详解

    下面小编就为大家带来一篇C#学习笔记- 随机函数Random()的用法详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • 经典实例讲解C#递归算法

    这篇文章主要用实例讲解C#递归算法的概念以及用法,文中代码非常详细,帮助大家更好的参考和学习,感兴趣的朋友可以了解下...2020-06-25
  • C#中list用法实例

    这篇文章主要介绍了C#中list用法,结合实例形式分析了C#中list排序、运算、转换等常见操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25