本站内容版权属于本人。转载须告知本人,写明出处,并在文首提供指向本站对应文章的链接。
本文链接:C#中new Task中使用async lambda表达式后start的一个坑
本文链接:C#中new Task中使用async lambda表达式后start的一个坑
C#中的async/await/Task机制是个非常方便的功能,可以将异步的功能写成像同步一样易懂。在其背后,编译器做了一些脏活,比如将async方法从await的地方切开成多个方法,按顺序在线程池中运行。这其中也暗藏了一些坑,如果不是非常有经验的程序员可能就掉进去了。
比如如下的程序,会输出什么呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Task t = new Task(async () => { Console.WriteLine("Task t start"); await Task.Delay(1000); Console.WriteLine("Task t end"); }); t.Start(); Task.Run(async () => { Console.WriteLine("Anonymous task start"); await t; Console.WriteLine("Anonymous task end"); }); Thread.Sleep(1200); |
这个程序开了两个Task,第一个等待1秒,第二个等待第一个,所以按一般的想法输出应该是:
1 2 3 4 5 |
Task t start Anonymous task start // 前两句顺序可能会变 // 1秒后 Task t end Anonymous task end |
然而,运行程序之后实际会得到:
1 2 3 4 5 |
Task t start Anonymous task start // 前两句顺序可能会变 Anonymous task end // 1秒后 Task t end |
这和想得不一样啊!后面那个Task根本就没等前一个运行完!然而,如果在后一个Task的最后加上代码检查t.IsComplete
的话,会发现确实是true
,所以不是后面这个Task的问题。
值得注意的是,前面那个Task使用了new Task
来初始化,而不是常用的Task.Run
,换成Task.Run
之后,问题解决。真相只有一个,那就是new Task
有问题!
查看new Task
的方法签名,实际使用的是:
1 |
public Task(Action action); |
而Task.Run
实际使用的是:
1 |
public static Task Run(Func<Task> function); |
谜团到此解开。对于async的无参数无返回值lambda表达式来说,实际上这个表达式的类型是Func<Task>
,也就是返回Task
类型,其中包括了这个Task必要的信息。虽然这个类型也兼容Action
,就如同async方法可以返回void
,但Task信息会丢失,运行后无法得知这个Task的运行进程,只能在第一个await前就认为它运行结束了,这就产生了最开始的结果。
为什么没有自动使用下面这个方法呢?
1 |
public Task(Func<Task> function); |
因为没有。
解决方法就是,如果使用了async的lambda表达式,就不要再用new Task
了,用Task.Run
吧。