Parallel.ForEach失败,显示“对象引用未设置为对象的实例”例外

问题描述

非常简单的Parallel.ForEach调用有时会失败,并显示

对象引用未设置为对象的实例。

库中某处而不是我的代码中的

exception。这是堆栈跟踪。

  at System.Threading.Tasks.TaskReplicator.Run[TState](ReplicatableuserAction`1 action,ParallelOptions options,Boolean stopOnFirstFailure)
   at System.Threading.Tasks.Parallel.PartitionerForEachWorker[TSource,TLocal](Partitioner`1 source,ParallelOptions parallelOptions,Action`1 simpleBody,Action`2 bodyWithState,Action`3 bodyWithStateAndindex,Func`4 bodyWithStateAndLocal,Func`5 bodyWithEverything,Func`1 localInit,Action`1 localFinally)
--- End of stack trace from prevIoUs location where exception was thrown ---
   at System.Threading.Tasks.Parallel.ThrowSingleCancellationExceptionorOtherException(ICollection exceptions,CancellationToken cancelToken,Exception otherException)
   at System.Threading.Tasks.Parallel.PartitionerForEachWorker[TSource,Action`1 localFinally)
   at System.Threading.Tasks.Parallel.ForEachWorker[TSource,TLocal](IEnumerable`1 source,Action`1 body,Action`1 localFinally)
   at System.Threading.Tasks.Parallel.ForEach[TSource](IEnumerable`1 source,Action`1 body)
...

这种情况很少发生,我已经看到它只在Linux上的.NET Core 2.1 App上发生过两次,没有明显的原因,也没有再现的方法

这是我的简化代码

var chunkSize = 100;
var processingList = new List<int>();

// removed code here which fill the processing list with data. No nulls here for sure

Parallel.ForEach(
    ChunkIndexes(processingList.Count,chunkSize),(interval) =>
    {
        var worker = new MyWorker();
        worker.Process(processingList,interval.startIndex,interval.endindex);
    });
// Function called by Parallel.ForEach()
public static IEnumerable<(int startIndex,int endindex)> ChunkIndexes(int totalCount,int chunkSize)
{
    for (var i = 0; i < totalCount; i += chunkSize)
    {
        yield return (i,Math.Min(i + chunkSize,totalCount));
    }
}

Parallel.ForEach中的代码在列表的不同部分上工作,并且仅修改值而不以任何方式更改列表大小。 没有异步/等待代码,也没有文件访问权限,也没有网络调用。它看起来非常简单,我只是无法弄清楚它可能由于任何原因而失败。有什么想法吗?

解决方法

基于我自己的代码中Parallel.ForEach的其他用法,故障堆栈跟踪以及下面的注释,我做了一些小的代码更改。到目前为止一切都很好。

基于您的StackTrace并基于TaskReplicator的源代码。运行这些选项为null或复制器的_pendigReplicas – Peter Csala 8月27日在14:48

以下是原始源代码,在极少数情况下可能会失败:

Parallel.ForEach(
    ChunkIndexes(processingList.Count,chunkSize),(interval) => {... code here ... });

修改后的代码。仅添加了ParallelOptions参数:

Parallel.ForEach(
    ChunkIndexes(processingList.Count,new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount },(interval) => {... code here ... });

仅当在运行使用.net core 2.1编译的C#代码时,才在Linux上观察到原始问题。