我启动了一些并行任务,如下所示:
var tasks =
Enumerable.Range(1, 500)
.Select(i => Task.Factory.StartNew<int>(ProduceSomeMagicIntValue))
.ToArray();
然后加入他们
Task.WaitAll(tasks);
在最后一行,我在tasks
下得到一个蓝色波浪形标记,并带有警告信息:
从Task []到Task []的共变量数组转换可能导致写操作的运行时异常。
我理解为什么我会收到这条消息,但有没有办法解决这个问题呢? (例如,像Task.WaitAll()
的通用版本?)
即使有警告,我也很确定这是一个安全的操作,但是如果你真的想要绕过它比创建你自己的实现更好的选择只是将你的tasks
参数转换成它想要的类型:
Task.WaitAll(tasks.Cast<Task>().ToArray())
为我杀死了蓝色的波浪形,让我保持我的tasks
变量通用,并没有强迫我创建一大堆新的可怕代码,这些代码最终是不必要的。
Task.WaitAll的一般方法意味着所有任务都必须返回相同的类型,这将是极其有限的用途。写这样的东西可以手动完成(参见Bas Brekelmans的答案),但这不允许ContinueWith或取消没有很多工作。
一个简单的解决方案,如果你没有使用该数组的其他任何东西是
.ToArray<Task>();
您可以创建一个扩展方法来执行此操作。
我不知道WaitAll的确切实现,但我们可以假设它等待每个项目完成:
static class TaskExtensions
{
public static void WaitAll<T>(this Task<T>[] tasks)
{
foreach (var item in tasks)
{
item.Wait();
}
}
}
然后从您当前的代码中调用:
tasks.WaitAll();
编辑
实际实现有点复杂。我已经省略了这个答案的代码,因为它相当长。
您可以修改它以支持通用任务。
实际上有一个类似的泛型过载:
Task all = Task.WhenAll(tasks)
这是不同的,它返回一个Task
,将在所有任务完成后完成。所以你可以使用await
,或Wait()
,无论你想要什么。
签名:
Overloads
---------非常规超载--------------
WhenAll(IEnumerable<Task>)
创建一个任务,该任务将在可枚举集合中的所有Task
对象完成时完成。
WhenAll(Task[])
创建一个任务,该任务将在数组中的所有Task
对象完成时完成。---------一般超载--------------
WhenAll<TResult>(IEnumerable<Task<TResult>>)
创建一个任务,该任务将在可枚举集合中的所有Task<TResult>
对象完成时完成。
WhenAll<TResult>(Task<TResult>[])
创建一个任务,该任务将在数组中的所有Task<TResult>
对象完成时完成。