我们可以使用Parallel.ForEach()将新元素添加到列表中吗?

时间:2012-04-13 23:07:48

标签: c# multithreading linq plinq

我的代码做了非常简单的事情

列表已经有元素。我在列表中有大约25000个元素(我希望有更多元素),每个元素都很小(DateTime)。

List<DateTime> newList = new List<DateTime>();
Parallel.ForEach(list, l => newlist.Add(new DateTime(l.Ticks + 5000)));

,即基于每个元素,我正在创建新元素并将它们添加到不同的列表中。 但是,这似乎不是一个好的编程方法。我点击了 一些 次,但不是每次都这样。

IndexOutOfRangeException : {"Index was outside the bounds of the array."}

我们可以使用Parallel.ForEach()将元素添加到列表中吗?如果是,为什么我会遇到错误?如果不是,为什么?

6 个答案:

答案 0 :(得分:6)

在这种情况下你真正想要的更像是这样:

newlist = list.AsParallel().Select(l => new DateTime(l.Ticks + 5000)).ToList();

虽然您应该衡量性能,看看这种情况是否会从并行化中受益。

答案 1 :(得分:5)

尝试一个带有最终结果的线程局部变量,将所有线程局部变量添加到newList中......

Parallel.ForEach(list, () => DateTime.MinValue, (l, state, date) =>
{
    date = new DateTime(l.Ticks+5000);
    return date;
},
finalresult =>
{
   lock (newList)
   {
       newList.Add(finalresult);
   }
});

第一个参数是你的旧列表,第二个参数是每个线程的初始值(我刚刚初始化为datetime min)。第三个参数块如下 - l与代码中的相同; state是一个Paralleloption对象,如果你选择,你可以退出并行循环;最后一个是代表线程局部变量的变量中的stand。 finalresult参数表示每个线程局部变量的最终结果,并为每个线程调用 - 在那里,您可以放置​​newList的锁并添加到newList共享变量。理论上这是有效的。我在自己的代码中使用了类似的编码。希望这可以帮助你或其他人。

答案 2 :(得分:2)

正如大家所提到的,似乎并没有这样做的并行。它肯定会远远慢得多。但是,为了完成,有时会失败的原因是多个线程正在写入的列表对象没有锁定。加上这个:

object _locker = new object();
List<DateTime> newList = new List<DateTime>();
Parallel.ForEach(list, l => lock (_locker) newlist.Add(new DateTime(l.Ticks + 5000)));

答案 3 :(得分:1)

没有足够的工作要做,以保证使用Parallel.ForEach并且List<T>也不是线程安全的,因此如果要并行添加到同一列表,则必须锁定。只需使用常规for循环。

答案 4 :(得分:1)

根据MSDN documentation for List<T>

,这将同时有效地调用List<T>.Add

“不保证所有实例成员都是线程安全的。”

即使它是(线程安全的),这也太便宜而无法从并行执行中受益(与并行执行的开销相反)。你真的测量过你的表现了吗? 25000个元素不是 很多。

答案 5 :(得分:1)

你真的需要列表中的这些吗?如果您只需要在foreach中枚举列表,那么您应该这样做,因为它将使用更少的内存:

IEnumerable<DateTime> newSequence = list.Select(d => new DateTime(d.Ticks + 5000));

如果你真的需要列表中的这些,只需在最后添加.ToList():

var newSequence = list.Select(d => new DateTime(d.Ticks + 5000)).ToList();

这几乎肯定足够快,你不需要并行化它。实际上,这可能比并行执行更快,因为它会有更好的内存性能。