await Task传异步Lambda问题
微软在.NET4.5中升级了C#语言到5.0,加入了await和async语法,极大地方便了广大开发人员的异步编程,也是为了和WinRT API配套,因为这套API充满了异步编程。
在开发过程中发现有时await不住?!流程还是往下走,觉得可能是使用有问题,于是进行了一下研究,发现了原因。
看下面的一组代码的运行结果及分析说明,WPF平台,代码在按钮的Click事件中。
1.
Debug. WriteLine( "1: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));Task. Factory. StartNew( async () =>{Debug. WriteLine( "2: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));await Task. Delay(5000);Debug. WriteLine( "3: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));});Debug. WriteLine( "4: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));
点一下按钮等运行完再点一下按钮的运行结果为:
1: 10 03:24:03.478
4: 10 03:24:03.484
2: 6 03:24:03.508
3: 12 03:24:08.509
1: 10 03:24:12.721
2: 6 03:24:12.722
4: 10 03:24:12.722
3: 12 03:24:17.744
连点两下按钮的运行结果为:
1: 10 03:29:50.103
2: 16 03:29:50.104
4: 10 03:29:50.104
1: 10 03:29:50.265
4: 10 03:29:50.266
2: 16 03:29:50.266
3: 17 03:29:55.125
3: 16 03:29:55.289
分析:Task.Factory.StartNew,在不加await的情况下,如果传人异步的Lambda,可能先执行Task中的逻辑,也可能先执行后面的逻辑,不能确定,所以这种写法不对。
2.
Debug. WriteLine( "1: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));await Task. Factory. StartNew( async () =>{Debug. WriteLine( "2: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));await Task. Delay(5000);Debug. WriteLine( "3: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));});Debug. WriteLine( "4: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));
点一下按钮等运行完再点一下按钮的运行结果为:
1: 10 03:25:16.212
2: 14 03:25:16.215
4: 10 03:25:16.217
3: 14 03:25:21.217
1: 10 03:25:44.692
2: 15 03:25:44.694
4: 10 03:25:44.695
3: 16 03:25:49.696
连点两下按钮的运行结果为:
1: 10 03:31:13.661
2: 15 03:31:13.662
4: 10 03:31:13.663
1: 10 03:31:13.826
2: 15 03:31:13.827
4: 10 03:31:13.828
3: 15 03:31:18.663
3: 14 03:31:18.837
分析:Task.Factory.StartNew,加await的情况下,如果传人异步的Lambda,可以保证先执行Task中的逻辑,再执行后面的逻辑。等等!Task中的逻辑没有执行完,只执行了一部分,如果我们的目的是等Task中的逻辑执行完再执行后面的逻辑,这种写法也是错误的,会产生BUG。
那正确的写法是什么呢,在VS中把鼠标移到await Task. Factory. StartNew( async () =>的StartNew方法上,可以看到返回值是Task<Task>。Task<Task>是什么意思呢,意思是返回一个返回Task的Task。对Task<Task>进行await会得到Task,要想等待这个Task运行完,还得await。
3.
Debug. WriteLine( "1: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));await await Task. Factory. StartNew( async () =>{Debug. WriteLine( "2: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));await Task. Delay(5000);Debug. WriteLine( "3: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));});Debug. WriteLine( "4: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));
点一下按钮等运行完再点一下按钮的运行结果为:
1: 10 03:33:27.212
2: 12 03:33:27.213
3: 7 03:33:32.214
4: 10 03:33:32.214
1: 10 03:33:35.142
2: 12 03:33:35.143
3: 12 03:33:40.145
4: 10 03:33:40.146
连点两下按钮的运行结果为:
1: 10 03:34:57.375
2: 3 03:34:57.376
1: 10 03:34:57.556
2: 3 03:34:57.557
3: 3 03:35:02.377
4: 10 03:35:02.378
3: 3 03:35:02.557
4: 10 03:35:02.559
分析:果然加两个await会得到想要的效果。Task.Factory.StartNew,加await await的情况下,如果传人异步的Lambda,可以保证先执行Task中的全部逻辑,再执行后面的逻辑。
再来看其他情况。
4.
Debug. WriteLine( "1: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));Task. Factory. StartNew(() =>{Debug. WriteLine( "2: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));Task. Delay(5000). Wait();Debug. WriteLine( "3: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));});Debug. WriteLine( "4: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));
点一下按钮等运行完再点一下按钮的运行结果为:
1: 10 03:53:56.809
2: 14 03:53:56.809
4: 10 03:53:56.809
3: 14 03:54:01.831
1: 10 03:54:03.441
2: 3 03:54:03.441
4: 10 03:54:03.441
3: 3 03:54:08.463
连点两下按钮的运行结果为:
1: 10 03:54:42.533
4: 10 03:54:42.533
2: 12 03:54:42.535
1: 10 03:54:42.707
4: 10 03:54:42.708
2: 7 03:54:42.708
3: 12 03:54:47.536
3: 7 03:54:47.731
分析:Task.Factory.StartNew,不加await的情况下,如果传人普通的Lambda,也会出现先执行Task中的部分逻辑,就去执行后面的逻辑的情况。
在VS中把鼠标移到Task. Factory. StartNew(() =>的StartNew方法上,看到返回的是Task,Task需要await,没加await是不对的。
5.
Debug. WriteLine( "1: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));await Task. Factory. StartNew(() =>{Debug. WriteLine( "2: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));Task. Delay(5000). Wait();Debug. WriteLine( "3: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));});Debug. WriteLine( "4: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));
点一下按钮等运行完再点一下按钮的运行结果为:
1: 10 03:55:48.141
2: 16 03:55:48.143
3: 16 03:55:53.144
4: 10 03:55:53.145
1: 10 03:55:56.560
2: 16 03:55:56.561
3: 16 03:56:01.563
4: 10 03:56:01.564
连点两下按钮的运行结果为:
1: 10 04:01:34.54
2: 16 04:01:34.54
1: 10 04:01:34.218
2: 15 04:01:34.219
3: 16 04:01:39.56
4: 10 04:01:39.57
3: 15 04:01:39.227
4: 10 04:01:39.227
分析:Task.Factory.StartNew,加await的情况下,如果传人普通的Lambda,能保证先执行Task中的全部逻辑,然后再执行后面的逻辑。
能不能加两个await,答案是不能,那样不能通过编译。如果强行把Lambda改为异步Lambda,可以编译通过也能得到正确的结果,但Resharper会给出警告,这样做没有必要,也不合适。
结论:使用Task在背后线程中执行耗时逻辑以避免阻塞UI线程是合适的做法。传入一个Lambda可以方便地实现逻辑,增加代码的可读性。如果Lambda中全是同步逻辑,没有使用await,即为普通Lambda,这时对Task的执行使用await不会有问题。但如果Lambda中需要使用异步API或调用异步方法,就必须改为异步Lambda,这时对Task的执行必须加两个await,才能达到想要的效果,先执行Task中的全部逻辑,然后再执行后面的逻辑。
转载于:https://www.cnblogs.com/clockdotnet/p/4188429.html
await Task传异步Lambda问题相关推荐
- async await Task
一.使用Task 引用命名空间 using System.Threading.Tasks; 1.工厂方式 Task.Factory.StartNew(() => {Console.WriteLi ...
- 用 async/await 来处理异步
引用出处:https://www.cnblogs.com/SamWeb/p/8417940.html 昨天看了一篇vue的教程,作者用async/ await来发送异步请求,从服务端获取数据,代码很简 ...
- SAP PO上传异步接口(PO从对方中间表读取数据)
导语:最近的项目上出现了一个奇奇怪怪的需求,上传接口居然不是外围系统给我传输,而是他数据丢到他的中间表,然后PO去取过来,真就他不动,我自己动. 下面说一下需要怎么来实现吧,其实跟PO下传接口写入中间 ...
- C++ concurrency::task实现异步编程(Windows)
最近一直在看js.python.lua等脚本语言的异步编程,今天脑子一热突然想看看C++能否也有支持异步的相关类库,上网一搜还真的有 microsoft官方文档https://msdn.microso ...
- Async和Await如何简化异步编程几个实例
引言 C#5.0中async和await两个关键字,这两个关键字简化了异步编程,之所以简化了,还是因为编译器给我们做了更多的工作,下面就具体看看编译器到底在背后帮我们做了哪些复杂的工作的. 同步代码存 ...
- js callback promise async await 几种异步函数处理方式
***callback 这个是最常用的也是最简单的 ,比如在ajax网络请求中,返回请求完成返回的数据 回调函数就是把一个函数当成另一个函数的参数,可以传递函数内的局部变量,也可以异步完成一些操作, ...
- 使用 Task 简化异步编程
.Net 传统异步编程概述 .NET Framework 提供以下两种执行 I/O 绑定和计算绑定异步操作的标准模式: 异步编程模型 (APM),在该模型中异步操作由一对 Begin/End 方法(如 ...
- 关于在foreach中使用await 或者其他异步问题处理
背景 前几天在项目中有遇到一种情况,就是后台返回的一个树形结构的分类数的数据给到前端我这边,不过有需要调两个接口,一个接口是获取标签的tab,可以看成是一级的分类,一个接口就是用来获取所有的子集,因为 ...
- C#多线程---Task实现异步
一.场景 使用Task来进行累加操作. 二.例子-Task使用 1 using System; 2 using System.Collections.Generic; 3 using System.L ...
最新文章
- ubuntu设置不同的eigen版本
- 【Linux】13.linux内核切换
- python读取文件名存到list_批量读取文件夹中的文件名
- Eclipse配置自动补齐键为alt+/
- list清空的函数java,6-1 jmu-Java-05集合-List中指定元素的删除 (20分)
- 我是如何查找RFC官方资料的
- Php 中如何将内容写入log日志中
- SSH中常见jar包缺少错误
- getAttribute实例例java,Java AttributedCharacterIterator.getAttribute方法代码示例
- C# 将Word,Execl,PPT,Project, 文件转成PDF, 不依赖Office!!
- 锐捷长ping_锐捷交换机常用操作命令
- 【报告分享】2021年中国网络文学出海报告-艾瑞咨询(附下载)
- mro python_Python进阶-继承中的MRO与super
- 【CV系列】Retinex理论模型及其应用
- 打开360浏览器显示无法连接服务器错误,Win7系统360浏览器打开网页显示“网络连接错误,错误代码102”怎么办...
- 长截图或长图片如何按页面切分后打印或插入到Word文档中
- 2021年深圳市坪山区贴息贴保资助申请条件及材料,补贴100万元
- Mysql—索引③:优化篇(不仅仅是索引)
- SIM900A模块开发:通过GPRS连接OneNet平台发送GPS信息
- 日志自动分析和解析开源工具