有没有通用的任务

本文关键字:任务 有没有 | 更新日期: 2023-09-27 18:30:21

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

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

然后加入他们

Task.WaitAll(tasks);

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

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

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

有没有通用的任务

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

如果您不将数组用于其他任何用途,一个简单的解决方案是

  .ToArray<Task>();

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

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

这为我杀死了蓝色波浪线,让我保持tasks变量泛型,并且不会强迫我创建大量最终不必要的新可怕代码。

更好更简单的答案

实际上有一个类似的泛型重载:

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>对象都已完成时。

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

我不知道 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

您可以修改此设置以支持通用任务。