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

C#5.0推出了新语法,await与async,但相信大家还是很少使用它们。关于await与async有很多文章讲解,但有没有这样一种感觉,你看完后,总感觉这东西很不错,但用的时候,总是想不起来,或者不知道该怎么用。

为什么呢?我觉得大家的await与async的打开方式不正确。

正确的打开方式

首先看下使用约束。

1、await 只能在标记了async的函数内使用。

2、await 等待的函数必须标记async。

有没有感觉这是个循环?没错,这就是个循环。这也就是为什么大家不怎么用他们的原因。这个循环很讨厌,那么怎么破除这个循环呢?

【很简单,await等待的是线程,不是函数。】

不理解吗?没关系,接着看下去。

下面从头来讲解,首先看这么一组对比

public static int NoAsyncTest()
{
   return 1;
}
public static async Task<int> AsyncTest()
{
  return 1;
}

async Task<int>等于int

这意味着我们在正常调用这两个函数时,他们是等效的。那么用async Task<int>来修饰int目的是什么呢?

目的是为了让这个方法这样被调用 await AsyncTest(),但直接这样调用,并不会开启线程,那这样费劲的修饰是不是就没什么意义了呢。

当然不是,那什么时候会让 await AsyncTest()有意义呢?

我们接着往下看,修改AsyncTest如下。然后,此时再调用await AsyncTest(),你会神奇的发现,依然没有卵用。。。

Excute方法正常执行,而AsyncTest内运行的线程,自己执行自己的。

public static async void Excute()
 {
       Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
       await AsyncTest();
       Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
 }

 public static async Task<int> AsyncTest()
 {
        Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            return 1;
 }

别着急,我们稍作调整,在线程后面增加.GetAwaiter().GetResult()。这句话是干什么用的呢?是用来获取线程返回值的。

这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。

运行一下,我们将看下面的结果。

public static async Task<int> AsyncTest()
        {
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            return 1;
        }

 

但是,好像await AsyncTest();还是没启作用。没错,事实就是,他真的不会起作用。。。

那么怎么才能让他起作用呢?

首先,我们定义一个普通函数,他的返回值是一个Task,然后我们得到Task后,运行它,再用await等待这个Task。

于是我们就得到这样的结果。

 public static async void Excute()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            var waitTask = AsyncTestRun();
            waitTask.Start();
            int i = await waitTask;
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " i " + i);
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
        }
        public static Task<int> AsyncTestRun()
        {
            Task<int> t = new Task<int>(() => {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
                return 100;
            });
            return t;
        }

  

如图,这样写await AsyncTest();就起作用了。

所以,还是那句话,await等待的是线程,不是函数。

但在图里,我们发现很奇怪的一点,结束Excute也是线程3,而不是线程1。也就是说,Await会对线程进行优化。

下面看下两组代码的对比,让我们就更清楚的了解下Await。

第一组,使用await等待线程。

public static async void Excute()
{
   Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
   await SingleAwait();
   Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
}

public static async Task SingleAwait()
{
     Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest开始 " + DateTime.Now);
     await Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            await Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest结束 " + DateTime.Now);
            return;
}

第二组,使用等待线程结果,等待线程。

 public static async void Excute()
{
     Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            await SingleNoAwait();
     Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
        }
public static async Task SingleNoAwait()
{
      Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now);
       Task.Run(() =>
        {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now);
            return;
}

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

结语

await是一种很便捷的语法,他的确会让代码简洁一些,但他主动优化线程的功能,如果不了解就使用,可能会导致一些奇怪的BUG发生。

这也是官方为什么只提供了await调用服务的例子,因为,在程序内调用,await还是要了解后,再使用,才安全。

原文地址:https://www.cnblogs.com/kiba/p/9292904.html

时间: 2024-08-29 07:19:51

C#语法——await与async的正确打开方式的相关文章

【分享】WeX5的正确打开方式(7)——数据组件详解

本文是[WeX5的正确打开方式]系列的第7篇文章,详细介绍WeX5中数据组件的增删改查以及数据定位方法. 前言 上一篇 数据组件初探 我们简单介绍了数据组件的引入和底层结构以及特性,并实现了简单的数据展示和数据改动.上一篇中简单提到了,数据组件就是后台数据库表在前端的一个映射,前后端开发人员只要预定好数据组件的内容,就可以简单实现前后端开发的分离.既然数据组件是一个数据库表的映射,那它的基本结构就是一张 2 维表格,举个栗子,定义一个 html5开发工具 的数据组件,xid 为 devtool

关于格子之表白的正确打开方式

飞碟说69期:<表白的正确打开方式>优酷视频在线观看.你可能想不通, 我身高177.体重117.长相不错.家庭不错.成绩好.体育好.器大活还好,为啥她不喜欢我?飞碟君教你三招: 知己知彼才好下套.自恋和自卑是表白大忌.再牛的技术也比不上真情流露 .自古表白多白表,要经历多少次“十动然拒”,才能学会表白的正确打开方式? 飞碟说谈恋爱系列: 表白的正确打开方式 文字版台词 闽江学院男生赖国森向他心仪的蕾蕾,送出了一封212天写的16万字的情书,内容是回忆两人一起吃过的沙县,他将其装订成册,并取名&

Xcode 的正确打开方式——Debugging(转载)

Xcode 的正确打开方式——Debugging 程序员日常开发中有大量时间都会花费在 debug 上,从事 iOS 开发不可避免地需要使用 Xcode.这篇博客就主要介绍了 Xcode 中几种能够大幅提升代码调试效率的方式. “If debugging is the process of removing bugs, then programming must be the process of putting them in.”——Edsger W. Dijkstra 添加条件 有时候我们可

swf文件的正确打开方式

软件下载链接在此,可不必看下面的解释 最近在看新东方的英语口语视频教程(<新东方4+1英语口语教程>,也在此向大家推荐一下),可是在播放过程中,老是出现自己跳转到视频的起始位置并暂停的状况,很让人抓狂,我试了好几款视频播放器,结果都是一样.想着可能是视频文件的问题吧,也就没管它了,迁就着看了几集. 可是,第二天我贼心不死,百度之.在筛掉几个错误答案之后,发现了这款ZzFlash播放器,下载试了下,视频果然不跳了. 原来视频跳转的原因是:课程设定有跟读的内容环节,一般的视频播放器没有识别出这个东

转载:淘宝前端团队的干货《论版本号的正确打开方式》

引用原文评论的一句话:条理清晰, 如此甚叼! 论版本号的正确打开方式 作者: 法海 发表于: 2016-08-04 版本号广泛运用于开发的各种场景:NPM 包的版本定义.对 NPM 包的特定版本的依赖指定.git 的 daily 版本号分支…… 面对如此多的场景,版本号的命名却存在很大问题.举些例子: 开始写一个新项目 / 模块时,不管三七二十一,都从 0.0.1 起版本,直到项目不再维护时,版本还停留在 0.0.48,前两位永远都是 0. API 变化巨大,而版本号雷打不动一步一个脚印.一个二

任务队列和异步接口的正确打开方式(.NET Core版本)

任务队列和异步接口的正确打开方式 什么是异步接口? Asynchronous Operations Certain types of operations might require processing of the request in an asynchronous manner (e.g. validating a bank account, processing an image, etc.) in order to avoid long delays on the client si

区块链技术是版权保护的“正确打开方式”

21世纪是互联网的时代,也是知识经济时代.在互联网发展成熟的现阶段,网络成为了众人获取信息.知识.消遣的主要渠道.越来越多的人开始进入新媒体,网络的创作者人数也日益增加,同时网络上也带来了大量的网络版权问题.今天墨者安全就给大家说一下版权保护方面的事情. 为什么说区块链技术是版权保护的正确打开方式呢? 首先是因为区块链独有的特性,有着去中心化.高安全性.不可篡改.公开透明的特性.通过区块链技术可以完整的记录作品和创作者的详细信息,并全部公开:所有的节点都可以共同记录,共同维护:并且盖上不可篡改的

【笔记】记一次.net语法await和async的异步编程实验与笔记。

1.实践代码全记录: using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace await_测试 { class Program { static void Main(string[] args) { te

【Android开发笔记】返回到上一个Activity的正确打开方式

技术支持 http://stackoverflow.com/questions/12276027/how-can-i-return-to-a-parent-activity-correctly 首先,在网上看到有人这么写: 1 @Override 2 public boolean onOptionsItemSelected(MenuItem item) { 3 switch (item.getItemId()) { 4 case android.R.id.home: 5 this.getActi