微软在.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问题相关推荐

  1. async await Task

    一.使用Task 引用命名空间 using System.Threading.Tasks; 1.工厂方式 Task.Factory.StartNew(() => {Console.WriteLi ...

  2. 用 async/await 来处理异步

    引用出处:https://www.cnblogs.com/SamWeb/p/8417940.html 昨天看了一篇vue的教程,作者用async/ await来发送异步请求,从服务端获取数据,代码很简 ...

  3. SAP PO上传异步接口(PO从对方中间表读取数据)

    导语:最近的项目上出现了一个奇奇怪怪的需求,上传接口居然不是外围系统给我传输,而是他数据丢到他的中间表,然后PO去取过来,真就他不动,我自己动. 下面说一下需要怎么来实现吧,其实跟PO下传接口写入中间 ...

  4. C++ concurrency::task实现异步编程(Windows)

    最近一直在看js.python.lua等脚本语言的异步编程,今天脑子一热突然想看看C++能否也有支持异步的相关类库,上网一搜还真的有 microsoft官方文档https://msdn.microso ...

  5. Async和Await如何简化异步编程几个实例

    引言 C#5.0中async和await两个关键字,这两个关键字简化了异步编程,之所以简化了,还是因为编译器给我们做了更多的工作,下面就具体看看编译器到底在背后帮我们做了哪些复杂的工作的. 同步代码存 ...

  6. js callback promise async await 几种异步函数处理方式

    ***callback  这个是最常用的也是最简单的 ,比如在ajax网络请求中,返回请求完成返回的数据 回调函数就是把一个函数当成另一个函数的参数,可以传递函数内的局部变量,也可以异步完成一些操作, ...

  7. 使用 Task 简化异步编程

    .Net 传统异步编程概述 .NET Framework 提供以下两种执行 I/O 绑定和计算绑定异步操作的标准模式: 异步编程模型 (APM),在该模型中异步操作由一对 Begin/End 方法(如 ...

  8. 关于在foreach中使用await 或者其他异步问题处理

    背景 前几天在项目中有遇到一种情况,就是后台返回的一个树形结构的分类数的数据给到前端我这边,不过有需要调两个接口,一个接口是获取标签的tab,可以看成是一级的分类,一个接口就是用来获取所有的子集,因为 ...

  9. C#多线程---Task实现异步

    一.场景 使用Task来进行累加操作. 二.例子-Task使用 1 using System; 2 using System.Collections.Generic; 3 using System.L ...

最新文章

  1. ubuntu设置不同的eigen版本
  2. 【Linux】13.linux内核切换
  3. python读取文件名存到list_批量读取文件夹中的文件名
  4. Eclipse配置自动补齐键为alt+/
  5. list清空的函数java,6-1 jmu-Java-05集合-List中指定元素的删除 (20分)
  6. 我是如何查找RFC官方资料的
  7. Php 中如何将内容写入log日志中
  8. SSH中常见jar包缺少错误
  9. getAttribute实例例java,Java AttributedCharacterIterator.getAttribute方法代码示例
  10. C# 将Word,Execl,PPT,Project, 文件转成PDF, 不依赖Office!!
  11. 锐捷长ping_锐捷交换机常用操作命令
  12. 【报告分享】2021年中国网络文学出海报告-艾瑞咨询(附下载)
  13. mro python_Python进阶-继承中的MRO与super
  14. 【CV系列】Retinex理论模型及其应用
  15. 打开360浏览器显示无法连接服务器错误,Win7系统360浏览器打开网页显示“网络连接错误,错误代码102”怎么办...
  16. 长截图或长图片如何按页面切分后打印或插入到Word文档中
  17. 2021年深圳市坪山区贴息贴保资助申请条件及材料,补贴100万元
  18. Mysql—索引③:优化篇(不仅仅是索引)
  19. SIM900A模块开发:通过GPRS连接OneNet平台发送GPS信息
  20. 日志自动分析和解析开源工具

热门文章

  1. javacript 数据类型
  2. python mmap
  3. math api matrix
  4. request.post
  5. 消息中间件学习总结(11)——Kafka与RocketMQ的Topic数量对单机性能的影响比较分析
  6. python records库_Python Records库使用举例
  7. 基 于 svm 的 图 像 分 类_CeO2和MgO助烧剂对矾土基莫来石合成料烧结的影响
  8. Spring3 MVC 深入研究
  9. 同一页面实现多个Tab选项卡功能
  10. 创建sdcard.img时,提示permission dennid