C#:为什么任务完成后要花很长时间才能获得任务t.Re​​sult?

时间:2018-08-15 15:03:33

标签: c# multithreading

我想知道为什么花这么长时间才能从已经完成的任务中获得结果。

List<Task<T>> myTasks = someList.Select(x => Task.Run(() => DoSomethingWith(x)));
Task.WaitAll(myTasks.ToArray());
var myResults = myTasks.Select(task => task.Result); // the line that takes too long

对这些单独的行进行计时表明,在最后一行中花费了大量时间(10个任务超过25ms)。在我看来,获得结果应该几乎是瞬时的,因为那时结果应该已经存在。在这种情况下是否有更好的方法来获得结果?

2 个答案:

答案 0 :(得分:4)

我会在您更新问题时进行更新,但这是我的最佳猜测。

此行无法编译,因为Select不会返回列表:

List<Task<T>> myTasks = someList.Select(x => Task.Run(() => DoSomethingWith(x)));

我要冒昧地猜测您实际上是在这样做:

var myTasks = someList.Select(x => Task.Run(() => DoSomethingWith(x)));

...会产生冷IEnumerable:只会在实际迭代结束时运行。

在上面的代码中,您在调用.ToArray()时对其进行迭代。但是,您描述为耗时25ms的那行同样只会产生另一个冷IEnumerable<>。这里没有任何实际的工作:

var myResults = myTasks.Select(task => task.Result);

所以我再次冒昧地猜测您正在做更多这样的事情:

var myResults = myTasks.Select(task => task.Result).ToList();

...将在myTasks上进行第二次迭代, ,导致Task.Run(...)再次被someList中的每个项目调用,然后等待所有这些任务完成。

换句话说,您要做两次工作,最后一次在您引用的行中进行一次。

幸运的是,使用任务并行库有更好的方法来完成您的工作。

var myResults = someList
    .AsParallel()
    .Select(x => DoSomethingWith(x))
    .ToList();

答案 1 :(得分:-1)

如果您确定第三行是导致速度下降的那一行,那么就没什么可做的了。这就是所有运行时库代码,并且在进行优化时,MS运行得非常紧密。

您可能希望手动遍历myTasks以避免选择代码,但是如果您花了几毫秒的时间,我会感到惊讶。