对于 C# 中 Task 的 StartNew 与 WhenAll 相互配合的实验
一、起因
最近写了一段需要等待几个任务(Task)执行完毕的代码,其中任务是使用 Task.Factory.StartNew 的形式。为什么不用 Task.Run 呢?因为这些任务可能耗时较长,由于 Task 默认是基于线程池的,为了避免耗时较长的任务挤占了软件中其它任务的生存空间,所以采用了给 StartNew 方法传参 TaskCreationOptions.LongRunning,让它单独起一个线程而不是从线程池中取,形如下图:
然后等待是使用的 Task.WhenAll,本来是打算使用 Task.WaitAll 的,因为要用到异步,所以换用了前者,如上图所示。
二、疑惑
由于 WhenAll 之前没用过,所以在网上搜索一下,看看是否有用得不对的地方。看到了这篇文章《C# Task 使用 WhenAll 和 WaitAll 需要注意的坑》,主要提到了两个坑:
1. 必须添加超时时间,防止无限等待。 2. 等待的 Task 一定要保证是启动的。
第一点还是比较好理解的,看看第二点是怎么说的吧:
按照作者的说法:
Task.Factory.StartNew 和 Task.Run 区别之一就有 Task.Run 会自动执行 Unwrap 操作
也就是说 Task.Factory.StartNew 比 Task.Run 少了自动解包,所以我们需要自己加上 Unwrap() 方法。
作者应该是个大佬,说的应该都是有道理的,但是有一个疑惑点在这篇文章中没有找到解答:看作者的 StartNew 中的方法都是异步的,也就是有 async 标志,那么问题就来了,如果是同步的方法,也就是没有 async 的情况,还需不需要 Unwrap 呢?
猜测是不需要的,因为同步方法的情况,应该是没有被“包装”的。不过作者没有提到这方面,为了严谨起见,决定自己写个测试程序实验一下。
三、实验
在之前做的 Task 测试程序中添加本次需要测试的部分:
如图所示,本次将要演示五种情况,每种情况最后都是用 await Task.WhenAll 进行等待,就不赘述了,直接看不同之处:
1、单纯的 Task.Factory.StartNew 方法(后面简称为 StartNew 方法);
2、StartNew 方法中启动异步方法(带 async,后同);
3、StartNew 方法中启动异步方法,在 StartNew 方法后带上 Unwrap() 方法的调用;
4、单纯的 Task.Run 方法;
5、Task.Run 方法中启动异步方法。
有人可能就要问了,Task.Run 组怎么少了个调用 Unwrap() 方法的情况呢?不是我漏了,也不是我不想加,而是加上的话 VS 就提示错误了,毕竟那样的话应该就属于过度解包了,所以加不得。
先来看看 StartNew 组:
再来看看 Task.Run 组:
接下来就是沙场秋点兵了:
结果很明显了,除了 2 号情况(StartNew async WhenAll)异常,其它情况都是正常的(也就是 await Task.WhenAll 能起到等待所有任务结束的作用)。
然后 3 号情况,就是在 2 号情况的基础上加上了 Unwrap 方法进行解包,就轻松挽回了败局,值得称道,给个特写:
四、结论
通过实验程序,可以得出如下结论:
1、单纯的 Task.Factory.StartNew 方法(内部启动同步方法的情况),以及任意的 Task.Run 方法(无论内部是同步方法还是异步方法),配合 await Task.WhenAll 都能达到预期效果。
2、Task.Factory.StartNew 方法中启动的是异步方法时,配合 await Task.WhenAll 方法达不到等待所有任务完成的效果;如果需要达到预期效果,需要在最后加上 Unwrap 方法。
另外,发现 Task.CurrentId 在这种情况的异步方法中无法获取值,Thread.CurrentThread.ManagedThreadId 则发挥稳定。
至于其它的组合情况以及其它发现,大家可以自行探索尝试。
五、资源
本文测试程序源码地址:https://gitee.com/dlgcy/DLGCY_WPFPractice/tree/Blog20230328
另外,说个题外话,本人自用的 WPF 类库 WPFTemplateLib 已打包发布到 NuGet 库中,欢迎大家安装使用:
https://www.nuget.org/packages/WPFTemplateLib/
感谢阅读。
原创文章,转载请注明: 转载自 独立观察员(dlgcy.com)
本文链接地址: [对于 C# 中 Task 的 StartNew 与 WhenAll 相互配合的实验](https://dlgcy.com/csharp-task-startnew-whenall-unwrap/)
关注微信公众号 独立观察员博客(DLGCY_BLOG) 第一时间获取最新文章
发表评论