OutOfRangeException with Parallel.For
本文关键字:For Parallel with OutOfRangeException | 更新日期: 2023-09-27 17:55:29
好的,所以我有一个工作正常的程序。在它里面有一个可以并行化的 for 循环。所以我用Parallel.For
来这样做。它工作正常一两次,但其他时候有以下例外:
未指定的错误 发生一个或多个错误
没有进一步的信息,只有这个有用的消息。有人知道会发生什么吗?
编辑:好的,所以我把它确定为一个超出范围的异常。事实证明,我在初始化数组元素之前正在访问它们,这似乎是一种竞争条件。我有这个:
Parallel.For(0, 4, (i, state) =>
{
levelTwoPermutationObjects.Add(new permutationObject());
levelTwoPermutationObjects[i].element = number;
levelTwoPermutationObjects[i].DoThings();
});
这使得第二行和第三行访问一个显然还不存在的元素。我将元素初始化器移出并行循环(以便数组在被访问之前被初始化),现在它可以工作了。
迭代几乎彼此独立,除了 Add() 部分,这显然取决于它之前是否有另一个元素。
我冒着在黑暗中开枪的风险:levelTwoPermutationObjects
不是线程安全的(即List<T>
)。您应该改用命名空间System.Collections.Generic.Concurrent
的集合,例如 ConcurrentBag<T>
(因为没有线程安全版本的 List<T>
),因为您正在遭受竞争条件(请参阅此处的示例)与.Add
-call(在多线程中没有写入操作的读取是可以的):
public void Add(T item) {
if (_size == _items.Length) EnsureCapacity(_size + 1);
_items[_size++] = item;
_version++;
}
另请参阅 MSDN 上的备注:
对 List 执行多个读取操作是安全的,但如果在读取集合时对其进行修改,则可能会出现问题。若要确保线程安全,请在读取或写入操作期间锁定集合。若要使集合能够由多个线程访问以进行读取和写入,必须实现自己的同步。有关具有内置同步的集合,请参阅 System.Collections.Radius 中的类。有关固有线程安全的替代方法,请参阅 ImmutableList 类。
如果您不愿意或无法适应levelTwoPermutationObjects
类型,您还可以使用lock
语句,例如(危险不使用 - 仅用于演示):
var @lock = new object();
Parallel.For(0, 4, (i, state) =>
{
lock (@lock)
{
levelTwoPermutationObjects.Add(new permutationObject());
levelTwoPermutationObjects[i].element = number;
levelTwoPermutationObjects[i].DoThings();
}
});
但这会使Parallel.For
调用毫无用处。实际上,您应该像(如果我正确解释您的代码)一样调整您的代码:
var @lock = new object();
Parallel.For(0, 4, (i, state) =>
{
var permutationObject = new permutationObject
{
element = number
};
permutationObject.DoThings();
lock (@lock)
{
levelTwoPermutationObjects.Add(permutationObject);
}
});
如果permutationObject
.DoThings()
是长时间运行的操作,您应该触发并忘记调用,例如 Task.Run
而不是等待结果继续进行.Add
-call。
否则,您可以将处理链转换为将元素添加到集合的种子设定进程(这应该是短期运行操作)和处理进程(其中每次迭代都可以是长时间运行的操作),以避免争用条件,方法是仅在顺序写入后执行读取,例如:
var levelTwoPermutationObjects = Enumerable.Range(0, 4)
.Select(arg => new permutationObject
{
element = number
})
.ToList();
Parallel.ForEach(levelTwoPermutationObjects,
permutationObject => permutationObject.DoThings());