C#中new Task中使用async lambda表达式后start的一个坑

C#中new Task中使用async lambda表达式后start的一个坑

本站内容版权属于本人。转载须告知本人,写明出处,并在文首提供指向本站对应文章的链接。
本文链接:C#中new Task中使用async lambda表达式后start的一个坑

C#中的async/await/Task机制是个非常方便的功能,可以将异步的功能写成像同步一样易懂。在其背后,编译器做了一些脏活,比如将async方法从await的地方切开成多个方法,按顺序在线程池中运行。这其中也暗藏了一些坑,如果不是非常有经验的程序员可能就掉进去了。

比如如下的程序,会输出什么呢?

这个程序开了两个Task,第一个等待1秒,第二个等待第一个,所以按一般的想法输出应该是:

然而,运行程序之后实际会得到:

这和想得不一样啊!后面那个Task根本就没等前一个运行完!然而,如果在后一个Task的最后加上代码检查t.IsComplete的话,会发现确实是true,所以不是后面这个Task的问题。

值得注意的是,前面那个Task使用了new Task来初始化,而不是常用的Task.Run,换成Task.Run之后,问题解决。真相只有一个,那就是new Task有问题!

查看new Task的方法签名,实际使用的是:

Task.Run实际使用的是:

谜团到此解开。对于async的无参数无返回值lambda表达式来说,实际上这个表达式的类型是Func<Task>,也就是返回Task类型,其中包括了这个Task必要的信息。虽然这个类型也兼容Action,就如同async方法可以返回void,但Task信息会丢失,运行后无法得知这个Task的运行进程,只能在第一个await前就认为它运行结束了,这就产生了最开始的结果。

为什么没有自动使用下面这个方法呢?

因为没有

解决方法就是,如果使用了async的lambda表达式,就不要再用new Task了,用Task.Run吧。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

*

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据