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

我的代码做的很简单

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

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

也就是说,基于每个元素,我正在创建新元素并将它们添加到不同的列表中。 但是,这似乎不是一个好的编程方法。 我有时会遇到这种exception,但不是每次都这样。

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

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

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

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

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

尝试一个带有最终结果的线程局部变量,将所有线程局部变量添加到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共享变量。 理论上这是有效的。 我在自己的代码中使用了类似的编码。 希望这可以帮助你或其他人。

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

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

没有足够的工作可以保证使用Parallel.ForEach并且List也不是线程安全的,所以如果你想并行添加到同一个列表,你必须锁定。 只需使用常规for循环。

这将有效地同时调用List.Add ,但是根据List MSDN文档 :

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

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

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

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

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

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

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