Parallel.For 的 OutOfRangeException
OutOfRangeException with Parallel.For
好的,所以我有一个运行正常的程序。它内部有一个可以并行化的 for 循环。所以我使用 Parallel.For
来这样做。它工作了一两次,但其他时候有以下异常:
Unspecified error One or more errors occurred
没有更多信息,只有这条有用的信息。有人知道会发生什么吗?
编辑:好的,所以我将其确定为超出范围的异常。原来我在初始化数组元素之前访问了它们,这看起来像是一个竞争条件。
我有这个:
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>
的线程安全版本),因为您遇到了竞争条件(请参阅 the example here) with the .Add
-call(在多线程中读不写操作是可以的):
public void Add(T item) {
if (_size == _items.Length) EnsureCapacity(_size + 1);
_items[_size++] = item;
_version++;
}
It is safe to perform multiple read operations on a List, but issues can occur if the collection is modified while it’s being read. To ensure thread safety, lock the collection during a read or write operation. To enable a collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization. For collections with built-in synchronization, see the classes in the System.Collections.Concurrent namespace. For an inherently thread–safe alternative, see the ImmutableList class.
如果您不愿意或不能调整 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
-call 无用。实际上,您应该像这样调整您的代码(如果我正确解释了您的代码):
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());
好的,所以我有一个运行正常的程序。它内部有一个可以并行化的 for 循环。所以我使用 Parallel.For
来这样做。它工作了一两次,但其他时候有以下异常:
Unspecified error One or more errors occurred
没有更多信息,只有这条有用的信息。有人知道会发生什么吗?
编辑:好的,所以我将其确定为超出范围的异常。原来我在初始化数组元素之前访问了它们,这看起来像是一个竞争条件。 我有这个:
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>
的线程安全版本),因为您遇到了竞争条件(请参阅 the example here) with the .Add
-call(在多线程中读不写操作是可以的):
public void Add(T item) {
if (_size == _items.Length) EnsureCapacity(_size + 1);
_items[_size++] = item;
_version++;
}
It is safe to perform multiple read operations on a List, but issues can occur if the collection is modified while it’s being read. To ensure thread safety, lock the collection during a read or write operation. To enable a collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization. For collections with built-in synchronization, see the classes in the System.Collections.Concurrent namespace. For an inherently thread–safe alternative, see the ImmutableList class.
如果您不愿意或不能调整 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
-call 无用。实际上,您应该像这样调整您的代码(如果我正确解释了您的代码):
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());