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还是要了解后,再使用,才安全。

C#语法——委托,架构的血液

C#语法——元组类型

C#语法——泛型的多种应用

----------------------------------------------------------------------------------------------------

注:此文章为原创,任何形式的转载都请联系作者获得授权并注明出处!
若您觉得这篇文章还不错,请点击下方的【推荐】,非常感谢!

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

  1. C#语法——泛型的多种应用 C#语法——await与async的正确打开方式 C#线程安全使用(五) C#语法——元组类型 好好耕耘 redis和memcached的区别

    C#语法——泛型的多种应用   本篇文章主要介绍泛型的应用. 泛型是.NET Framework 2.0 版类库就已经提供的语法,主要用于提高代码的可重用性.类型安全性和效率. 泛型的定义 下面定义了 ...

  2. Console控制台的正确打开方式

    Console控制台的正确打开方式 console对象提供了访问浏览器调试模式的信息到控制台 -- Console对象 |-- assert() 如果第一个参数断言为false,则在控制台输出错误信息 ...

  3. iOS开发小技巧--相机相册的正确打开方式

    iOS相机相册的正确打开方式- UIImagePickerController 通过指定sourceType来实现打开相册还是相机 UIImagePickerControllerSourceTypeP ...

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

    Xcode 的正确打开方式——Debugging   程序员日常开发中有大量时间都会花费在 debug 上,从事 iOS 开发不可避免地需要使用 Xcode.这篇博客就主要介绍了 Xcode 中几种能 ...

  5. InnoDB缓冲池预加载在MySQL 5.7中的正确打开方式

    InnoDB缓冲池预加载在MySQL 5.7中的正确打开方式 https://mp.weixin.qq.com/s/HGa_90XvC22anabiBF8AbQ 在这篇文章里,我将讨论在MySQL 5 ...

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

    任务队列和异步接口的正确打开方式 什么是异步接口? Asynchronous Operations Certain types of operations might require processi ...

  7. (一)Redis for Windows正确打开方式

    目录 (一)Redis for Windows正确打开方式 (二)Redis for 阿里云公网连接 (三)Redis for StackExchange.Redis 下载地址 官网.中文网1 及 中 ...

  8. List的remove()方法的三种正确打开方式

    转: java编程:List的remove()方法的三种正确打开方式! 2018年08月12日 16:26:13 Aries9986 阅读数 2728更多 分类专栏: leetcode刷题   版权声 ...

  9. C++11随机数的正确打开方式

    C++11随机数的正确打开方式 在C++11之前,现有的随机数函数都存在一个问题:在利用循环多次获取随机数时,如果程序运行过快或者使用了多线程等方法,srand((unsigned)time(null ...

随机推荐

  1. NOIP 2017 游记?

    Day -1 晚上被dg谈了谈人生,没有卵用 Day 0 早上又被老吕教训了一遍,想打板子,打印机还坏了,老吕又奶了一波题,后来发现一个都没中.之后就出发了,中午吃了点肯德基,妈妈来了,给我了个小袋子 ...

  2. bzoj 2653 middle 二分答案 主席树判定

    判断中位数是否可行需要将当前的解作为分界,大于其的置为1,小于为-1,然后b-c必选,ab,cd可不选,这个用线段树判定就好 但不能每次跑,所以套主席树,按权值排序,构建主席树,更新时将上一个节点改为 ...

  3. BZOJ_2161_布娃娃_权值线段树

    BZOJ_2161_布娃娃_权值线段树 Description 小时候的雨荨非常听话,是父母眼中的好孩子.在学校是老师的左右手,同学的好榜样.后来她成为艾利斯顿第二 代考神,这和小时候培养的良好素质是 ...

  4. BZOJ_1552_[Cerc2007]robotic sort_splay

    BZOJ_1552_[Cerc2007]robotic sort_splay 题意: 分析: splay维护区间操作 可以先把编号排序,给每个编号分配一个固定的点,映射过去 查找编号的排名时先找到这个 ...

  5. WebService-----Xfire

    Xfire相对Axis2 个人感觉相对简单一点,因为myEclipse上可以自动生成.而且在目前来讲,Xfire也是更受欢迎一点. 首先说明Xfire所用jar包:http://download.cs ...

  6. Postman----Newman的使用

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000; background-color: #fffff ...

  7. 【转】APP功能测试要领

    也许大家从事APP功能测试已经有一段时间了,心中一定有一个疑问,怎么样才能提高测试的覆盖面呢,我今天把APP功能测试内容分为APP本身的功能,APP关联的事务.APP外部环境.APP其他四大块来给大家 ...

  8. 玩转PHP中的正则表达式

    玩转PHP中的正则表达式 检验用户输入.解析用户输入和文件内容,以及重新格式化字符串 级别: 中级 正则表达式提供了一种处理文本的强大方法.使用正则表达式,您可以对用户输入进行复杂的检验.解析用户输入 ...

  9. 轻量化卷积神经网络MobileNet论文详解(V1&V2)

    本文是 Google 团队在 MobileNet 基础上提出的 MobileNetV2,其同样是一个轻量化卷积神经网络.目标主要是在提升现有算法的精度的同时也提升速度,以便加速深度网络在移动端的应用.

  10. 基于.NET CORE微服务框架 -谈谈surging 的messagepack、protobuffer、json.net 序列化

    1.前言 surging内部使用的是高性能RPC远程服务调用,如果用json.net序列化肯定性能上达不到最优,所以后面扩展了protobuf,messagepack序列化组件,以支持RPC二进制传输 ...