我的代码做了非常简单的事情
列表已经有元素。我在列表中有大约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()将元素添加到列表中吗?如果是,为什么我会遇到错误?如果不是,为什么?
答案 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();
这几乎肯定足够快,你不需要并行化它。实际上,这可能比并行执行更快,因为它会有更好的内存性能。