learun开发社区 - 力软快速开发平台官方论坛

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 967|回复: 3

C#语法:await与async的正确打开方式

[复制链接]

1

主题

1

帖子

31

积分

新手上路

Rank: 1

积分
31
发表于 2019-3-1 13:28:52 | 显示全部楼层 |阅读模式
C#5.0推出了新语法,await与async,但相信大家还是很少使用它们。关于await与async有很多文章讲解,但有没有这样一种感觉,你看完后,总感觉这东西很不错,但用的时候,总是想不起来,或者不知道该怎么用。
为什么呢?我觉得大家的await与async的打开方式不正确。

正确的打开方式


首先看下使用约束。
1、await 只能在标记了async的函数内使用。
2、await 等待的函数必须标记async。
有没有感觉这是个循环?没错,这就是个循环。这也就是为什么大家不怎么用他们的原因。这个循环很讨厌,那么怎么破除这个循环呢?
【很简单,await等待的是线程,不是函数。】
不理解吗?没关系,接着看下去。
下面从头来讲解,首先看这么一组对比
  1. public static int NoAsyncTest()
  2. {
  3.    return 1;
  4. }
  5. public static async Task<int> AsyncTest()
  6. {
  7.   return 1;
  8. }
复制代码






async Task<int>等于int
这意味着我们在正常调用这两个函数时,他们是等效的。那么用async Task<int>来修饰int目的是什么呢?
目的是为了让这个方法这样被调用 await AsyncTest(),但直接这样调用,并不会开启线程,那这样费劲的修饰是不是就没什么意义了呢。
当然不是,那什么时候会让 await AsyncTest()有意义呢?
我们接着往下看,修改AsyncTest如下。然后,此时再调用await AsyncTest(),你会神奇的发现,依然没有卵用。。。
Excute方法正常执行,而AsyncTest内运行的线程,自己执行自己的。
  1. public static async void Excute()
  2. {
  3.        Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
  4.        await AsyncTest();
  5.        Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
  6. }

  7. public static async Task<int> AsyncTest()
  8. {
  9.         Task.Run(() =>
  10.             {
  11.                 Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
  12.                 Thread.Sleep(1000);
  13.             });
  14.             return 1;
  15. }
复制代码







别着急,我们稍作调整,在线程后面增加.GetAwaiter().GetResult()。这句话是干什么用的呢?是用来获取线程返回值的。
这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。
运行一下,我们将看下面的结果。
  1. public static async Task<int> AsyncTest()
  2.         {
  3.             Task.Run(() =>
  4.             {
  5.                 Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
  6.                 Thread.Sleep(1000);
  7.             }).GetAwaiter().GetResult();
  8.             return 1;
  9.         }
复制代码







 
但是,好像await AsyncTest();还是没启作用。没错,事实就是,他真的不会起作用。。。
那么怎么才能让他起作用呢?
首先,我们定义一个普通函数,他的返回值是一个Task,然后我们得到Task后,运行它,再用await等待这个Task。
于是我们就得到这样的结果。
  1. public static async void Excute()
  2.        {
  3.            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
  4.            var waitTask = AsyncTestRun();
  5.            waitTask.Start();
  6.            int i = await waitTask;
  7.            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " i " + i);
  8.            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
  9.        }
  10.        public static Task<int> AsyncTestRun()
  11.        {
  12.            Task<int> t = new Task<int>(() => {
  13.                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
  14.                Thread.Sleep(1000);
  15.                return 100;
  16.            });
  17.            return t;
  18.        }
复制代码







  
如图,这样写await AsyncTest();就起作用了。
所以,还是那句话,await等待的是线程,不是函数。
但在图里,我们发现很奇怪的一点,结束Excute也是线程3,而不是线程1。也就是说,Await会对线程进行优化。
下面看下两组代码的对比,让我们就更清楚的了解下Await。
第一组,使用await等待线程。
  1. public static async void Excute()
  2. {
  3.    Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
  4.    await SingleAwait();
  5.    Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
  6. }
  7.       
  8. public static async Task SingleAwait()
  9. {
  10.      Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest开始 " + DateTime.Now);
  11.      await Task.Run(() =>
  12.             {
  13.                 Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
  14.                 Thread.Sleep(1000);
  15.             });
  16.             await Task.Run(() =>
  17.             {
  18.                 Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
  19.                 Thread.Sleep(1000);
  20.             });
  21.             Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest结束 " + DateTime.Now);
  22.             return;
  23. }
复制代码







第二组,使用等待线程结果,等待线程。
  1. public static async void Excute()
  2. {
  3.      Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
  4.             await SingleNoAwait();
  5.      Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
  6.         }
  7. public static async Task SingleNoAwait()
  8. {
  9.       Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now);
  10.        Task.Run(() =>
  11.         {
  12.                 Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
  13.                 Thread.Sleep(1000);
  14.             }).GetAwaiter().GetResult();
  15.             Task.Run(() =>
  16.             {
  17.                 Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
  18.                 Thread.Sleep(1000);
  19.             }).GetAwaiter().GetResult();
  20.             Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now);
  21.             return;
  22. }
复制代码







可以明确的看到,第二组,线程重新回到了主线程1中,而第一组,已经被优化到了线程4中。

结语
await是一种很便捷的语法,他的确会让代码简洁一些,但他主动优化线程的功能,如果不了解就使用,可能会导致一些奇怪的BUG发生。
这也是官方为什么只提供了await调用服务的例子,因为,在程序内调用,await还是要了解后,再使用,才安全。
回复

使用道具 举报

0

主题

8

帖子

170

积分

注册会员

Rank: 2

积分
170
发表于 2019-3-1 14:05:28 | 显示全部楼层
感谢作者的无私分享,目前主流的技术干货教程
回复

使用道具 举报

0

主题

5

帖子

90

积分

注册会员

Rank: 2

积分
90
发表于 2019-3-19 16:14:19 | 显示全部楼层
感谢楼主分享,很实用
回复

使用道具 举报

0

主题

7

帖子

146

积分

注册会员

Rank: 2

积分
146
发表于 2019-3-25 07:57:06 | 显示全部楼层
感谢分享
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|learun开发社区 - 力软快速开发平台官方论坛 ( 沪ICP备14034717号 )

GMT+8, 2020-4-8 04:40 , Processed in 0.189559 second(s), 24 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表