70

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

var tasks =
    Enumerable.Range(1, 500)
    .Select(i => Task.Factory.StartNew<int>(ProduceSomeMagicIntValue))
    .ToArray();

然后加入他们

Task.WaitAll(tasks);

在最后一行中,我在 下得到一个蓝色波浪标记tasks,并带有警告消息:

从 Task[] 到 Task[] 的协变数组转换
可能导致写操作的运行时异常。

我明白为什么我会收到此消息,但有没有办法解决这个问题?(例如,像Task.WaitAll()? 的通用版本)

4

4 回答 4

33

Task.WaitAll 的通用方法意味着所有任务都必须返回相同的类型,这将非常有限。可以手动编写类似的内容(请参阅 Bas Brekelmans 的回答),但这不会允许 ContinueWith 或取消而无需大量工作。

如果您没有将数组用于其他任何事情,一个简单的解决方案是

  .ToArray<Task>();
于 2012-04-06T13:30:54.500 回答
29

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

Task.WaitAll(tasks.Cast<Task>().ToArray())

这对我来说消除了蓝色曲线,让我保持我的tasks变量通用,并且不会强迫我创建大量新的可怕代码,这些代码最终是不必要的。

于 2012-04-06T14:20:46.293 回答
19

更好更简单的答案

实际上有一个类似的通用重载

Task all = Task.WhenAll(tasks)

这是不同的,它返回一个Task将在所有任务完成后完成的。所以你可以使用await它,或者Wait(),任何你想要的。

看签名:

重载

--------- 非通用过载 --------------

WhenAll(IEnumerable<Task>) 创建一个将Task在可枚举集合中的所有对象都完成时完成的任务。

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

---------通用过载--------------

WhenAll<TResult>(IEnumerable<Task<TResult>>) 创建一个将Task<TResult>在可枚举集合中的所有对象都完成时完成的任务。

WhenAll<TResult>(Task<TResult>[]) 创建一个任务,该任务将Task<TResult>在数组中的所有对象都完成后完成。

于 2019-03-13T10:51:16.657 回答
6

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

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

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

然后从您当前的代码中调用:

tasks.WaitAll();

编辑

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

http://pastebin.com/u30PmrdS

您可以对其进行修改以支持通用任务。

于 2012-04-06T13:36:03.543 回答