Parallel.ForEach 失败并出现 "Object reference not set to an instance of an object." 异常

Parallel.ForEach fails with "Object reference not set to an instance of an object." exception

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

Object reference not set to an instance of an object.

库中某处的异常,而不是我的代码中的异常。这是堆栈跟踪。

  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,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)
   at System.Threading.Tasks.Parallel.ForEachWorker[TSource,TLocal](IEnumerable`1 source, ParallelOptions parallelOptions, Action`1 body, Action`2 bodyWithState, Action`3 bodyWithStateAndIndex, Func`4 bodyWithStateAndLocal, Func`5 bodyWithEverything, Func`1 localInit, Action`1 localFinally)
   at System.Threading.Tasks.Parallel.ForEach[TSource](IEnumerable`1 source, Action`1 body)
...

它很少发生,我只在 Linux 的 .NET Core 2.1 应用程序上看到它发生过几次,没有明显的原因,也无法重现。

这是我的简化代码:

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 中的代码正在处理列表的不同部分,并且仅修改值而不以任何方式更改列表大小。 没有 async/await 代码,没有文件访问权限,也没有网络调用。它看起来非常简单,我就是想不通它怎么会因为任何原因而失败。有什么想法吗?

基于 Parallel.ForEach 在我自己的代码中的其他用途、失败堆栈跟踪和下面的注释,我对代码做了一个小改动。到目前为止一切看起来都很好。

Based on your StackTrace and based on the source code of TaskReplicator.Run either the options is null or the replicator's _pendigReplicas – Peter Csala Aug 27 at 14:48

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

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

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

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

当 运行 C# 代码编译 with.net 核心 2.1 时,仅在 Linux 上观察到原始问题。