partitioner抛出invalidoperationexception的根本原因是其依赖的数据源在并行划分过程中被外部修改,导致内部状态不一致。1. 当使用partitioner.create处理非线程安全集合(如list<t>)时,若另一线程在parallel.foreach执行期间添加、删除或修改集合元素,partitioner原先计算的分区索引将失效,从而触发异常;2. 解决方案是确保数据源稳定,最有效方法是在传递给partitioner前调用toarray()或tolist()创建副本,使并行操作基于不可变快照进行;3. 若数据源需动态更新,应改用concurrentbag<t>、concurrentqueue<t>等线程安全集合,它们能安全支持并发读写并兼容partitioner;4. 避免在并行处理中直接修改原始数据源,而应将结果写入线程安全的收集器(如concurrentbag);5. 不同partitioner.create重载中,数组和ilist版本因支持索引访问而性能更优,但同样要求结构稳定,而带enumerablepartitioneroptions的重载可控制缓冲行为,自定义orderablepartitioner则需自行保证线程安全。总之,该异常是系统对数据不一致的保护机制,通过使用数据副本或线程安全集合即可可靠避免。

C#中
Partitioner
InvalidOperationException
Partitioner
遇到
Partitioner
InvalidOperationException
一种最直接、也最常用的方法,就是在将集合传递给
Partitioner
List<T>
Parallel.ForEach
List<int> originalList = new List<int> { 1, 2, 3, 4, 5 };
// ... 某个地方可能会修改originalList
// 在传递给Partitioner之前,创建一个副本
var stableSource = originalList.ToArray(); // 或者 .ToList()
// 现在,使用这个稳定的副本进行并行处理
Parallel.ForEach(Partitioner.Create(stableSource), item =>
{
// 对item进行操作
Console.WriteLine($"Processing {item}");
// 在这里不要修改originalList或stableSource
});这样做的好处是,
Partitioner
InvalidOperationException
如果你的数据源必须是动态的,并且在并行处理期间会有并发修改,那么你需要考虑使用专门为并发设计的集合类型,比如
ConcurrentBag<T>
ConcurrentQueue<T>
Partitioner
// 假设这是一个可能被并发修改的集合
ConcurrentBag<string> concurrentData = new ConcurrentBag<string>();
concurrentData.Add("Alpha");
concurrentData.Add("Beta");
// ... 可以在其他线程继续添加或移除
// Partitioner.Create可以直接处理ConcurrentBag
Parallel.ForEach(Partitioner.Create(concurrentData), item =>
{
Console.WriteLine($"Processing {item}");
// 在这里对concurrentData进行修改通常是安全的,但要理解其语义
});总而言之,问题的根源在于并行处理对数据源一致性的要求,解决方案则围绕着如何提供一个满足这一要求的数据源展开。
嗯,这事儿挺常见的,说实话,我也踩过这个坑。
Partitioner
List<T>
Array
InvalidOperationException
具体来说,当你使用
Partitioner.Create(myList)
Partitioner
myList
myList
它主要发生在以下几种情况:
Parallel.ForEach
List<T>
Dictionary<TKey, TValue>
OrderablePartitioner
GetPartitions
GetDynamicPartitions
这确实是一个常见的需求,但“安全地在并行处理中修改数据源”这个说法本身就有点陷阱。更准确的说法应该是:如何在并行处理中收集结果,或者在并行处理中处理动态变化的数据。直接修改作为
Partitioner
InvalidOperationException
如果你需要在并行处理中产生新的数据,并把这些数据收集起来,你应该使用线程安全的集合来存储结果,而不是去修改原始的数据源。例如:
List<int> numbers = Enumerable.Range(1, 100).ToList();
// 使用ConcurrentBag来收集并行处理的结果
ConcurrentBag<double> results = new ConcurrentBag<double>();
Parallel.ForEach(Partitioner.Create(numbers), number =>
{
// 假设这是一个耗时的计算
double result = Math.Sqrt(number) * 10;
results.Add(result); // 安全地将结果添加到线程安全集合中
});
// 所有并行任务完成后,可以安全地访问results
foreach (var res in results)
{
Console.WriteLine(res);
}这里,
numbers
Parallel.ForEach
results
如果你的“数据源”本身就是动态的,比如一个队列,你希望在并行处理的同时,有新的数据不断地被添加到队列中,并且能够被处理,那么你需要从一开始就选择一个线程安全的集合作为数据源,并且
Partitioner
ConcurrentQueue<T>
ConcurrentQueue<string> tasksQueue = new ConcurrentQueue<string>();
tasksQueue.Enqueue("Task A");
tasksQueue.Enqueue("Task B");
// 模拟另一个线程不断添加任务
Task.Run(() =>
{
for (int i = 0; i < 5; i++)
{
Thread.Sleep(500); // 模拟延迟
tasksQueue.Enqueue($"Dynamic Task {i}");
Console.WriteLine($"Added Dynamic Task {i}");
}
});
// Partitioner.Create可以直接处理ConcurrentQueue
// 注意:对于无限流或持续添加的队列,你可能需要一个停止条件
Parallel.ForEach(Partitioner.Create(tasksQueue), task =>
{
Console.WriteLine($"Processing: {task}");
Thread.Sleep(200); // 模拟处理时间
});
Console.WriteLine("All tasks processed."); // 这行可能在队列完全清空前出现在这种情况下,
Partitioner.Create(tasksQueue)
ConcurrentQueue
Parallel.ForEach
BlockingCollection
总的来说,避免在并行处理中直接修改作为
Partitioner
Partitioner.Create
Partitioner.Create<TSource>(IEnumerable<TSource> source)
IEnumerable<TSource>
source
source
List<T>
T[]
source
Parallel.ForEach
InvalidOperationException
Partitioner.Create<TSource>(IList<TSource> list)
IList<TSource>
IList
Partitioner
list
InvalidOperationException
Partitioner.Create<TSource>(TSource[] array)
TSource[]
Partitioner
List<T>
Partitioner
InvalidOperationException
InvalidOperationException
Partitioner.Create(int fromInclusive, int toExclusive)
Parallel.For(0, 100, i => { /* process item at index i */ });Partitioner.Create<TSource>(IEnumerable<TSource> source, EnumerablePartitionerOptions options)
IEnumerable<TSource>
EnumerablePartitionerOptions.NoBuffering
NoBuffering
Partitioner
OrderablePartitioner<TSource>
OrderablePartitioner
InvalidOperationException
总的来说,选择哪个重载取决于你的数据源类型、你对性能的需求以及你是否需要处理动态或并发修改的数据。对于大多数情况,如果数据源是静态的,
ToArray()
Partitioner.Create(TSource[])
ConcurrentBag
Partitioner.Create(IEnumerable<TSource>)
以上就是C#的Partitioner的InvalidOperationException是什么?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号