C# 是否有一个通用任务。WaitAll?

C# 是否有一个通用任务。WaitAll?,c#,generics,task-parallel-library,covariance,C#,Generics,Task Parallel Library,Covariance,我开始了一些并行任务,如下所示: var tasks = Enumerable.Range(1, 500) .Select(i => Task.Factory.StartNew<int>(ProduceSomeMagicIntValue)) .ToArray(); 在最后一行,我在任务下看到一个蓝色的波形标记,上面有一条警告消息: 从任务[]到任务[]的共变量数组转换 写入操作时可能导致运行时异常。 我明白为什么我会收到这个信息,但有办法吗?(例如,像

我开始了一些并行任务,如下所示:

var tasks =
    Enumerable.Range(1, 500)
    .Select(i => Task.Factory.StartNew<int>(ProduceSomeMagicIntValue))
    .ToArray();
在最后一行,我在
任务
下看到一个蓝色的波形标记,上面有一条警告消息:

从任务[]到任务[]的共变量数组转换 写入操作时可能导致运行时异常。


我明白为什么我会收到这个信息,但有办法吗?(例如,像
Task.WaitAll()
?)的通用版本一样,

Task.WaitAll的通用方法意味着所有任务都必须返回相同的类型,这将非常有限。这样的书写可以手动完成(见Bas Brekelmans的答案),但这不允许在没有大量工作的情况下继续或取消

如果您不将阵列用于其他任何用途,那么一个简单的解决方案是

  .ToArray<Task>();
.ToArray();

您可以创建一个扩展方法来执行此操作

我不知道WaitAll的具体实现,但我们可以假设它等待每个项目完成:

static class TaskExtensions
{
    public static void WaitAll<T>(this Task<T>[] tasks)
    {
        foreach (var item in tasks)
        {
            item.Wait();
        }
    }
}
编辑

实际实现稍微复杂一些。我从这个答案中省略了代码,因为它相当长


您可以修改此选项以支持一般任务。

我很确定,即使有警告,这也是一个安全的操作,但是如果您真的想绕过它,那么比创建自己的实现更好的选择就是将您的
任务
参数转换为它想要的类型:

Task.WaitAll(tasks.Cast<Task>().ToArray())
Task.WaitAll(tasks.Cast().ToArray())
这为我消除了蓝色的扭曲,让我保持我的
任务
变量的通用性,并且不会强迫我创建大量最终不必要的新的可怕代码。

一个更好更简单的答案 实际上,有一个类似的通用重载:

Task all = Task.WhenAll(tasks)
这与此不同,它返回一个
任务
,该任务将在所有任务完成后完成。因此,您可以在其上使用
Wait
,或
Wait()
,无论您想要什么

请看签名:

超载 ---------非泛型重载--------------

当所有(IEnumerable)
创建一个将 当可枚举集合中的所有
任务
对象 完成

whalll(Task[])
创建一个任务,该任务将在所有
任务
数组中的对象已完成

---------通用重载--------------

当所有(IEnumerable)
创建一个将 当可枚举中的所有
任务
对象 收集工作已经完成

whalll(Task[])
创建将完成的任务 当数组中的所有
任务
对象都已完成时


+1这是可能的,但在我的例子中,我实际上使用数组来获取
int
结果:
var results=tasks。选择(task=>task.Result)
@W0lf,然后只需在将其传递给task.WaitAll之前执行一个额外的.ToArray(),对该复制任务[]的任何可能分配都不会导致运行时异常在这种情况下,转换是安全的,因为
WaitAll()
不会写入数组。有什么原因要避免它吗?另外,.Net 4.5将包含一个返回单个
任务的函数,该函数在集合中的所有
任务完成时完成。而且它还有一个通用版本,它可以在
任务的任何
IEnumerable
上工作。看起来他们把你正在谈论的内容重命名为WhenAll,这样你就可以说“等待任务。WhenAll(任务1,任务2);”你有没有可能考虑接受一个不同的答案?我个人觉得其他人比你选择的“接受”的人更有价值。@Quibblesome完成了。这对我来说非常有效。使用
var ifAllTasksRanToCompletion=Task.WaitAll(tasks.Cast().ToArray(),timeout:TimeSpan.FromSeconds(20)),对运行到完成的任务和超时的任务进行测试。“实际实现”非常重要。更不用说,如果.NET Framework实现有任何错误修复或性能改进,那么您将失去控制(或需要重新编码)。@MerickOWA和DMac驱逐舰的解决方案(按顺序)非常简单地解决了OP的问题。这显然是错误的-您在这里连续运行任务,这不是真正的
WaitAll
所做的。@MarkAmery这假设任务在输入此方法时已被调度。如果人们坚持构建自己的(而不是使用其他解决方案之一将泛型集合转换为任务数组),这是一个更好的源,提供所有内部代码的完整注释和链接,您还需要复制这些代码才能使其正常工作:
tasks.WaitAll();
Task.WaitAll(tasks.Cast<Task>().ToArray())
Task all = Task.WhenAll(tasks)