如果源为空,Parallel.ForEach 是否启动线程?

Does Parallel.ForEach start threads if the source is empty?

我是这样使用 Parallel.ForEach 的:

public void myMethod(IEnumerable<MyType> paramIeCollection)
{
    Parallel.Foreach(paramIeCollection,
          (iterator) =>
          {
              //Do something
          });
}

我想知道当 paramIeCollection 为空时,Parallel.ForEach 是否仍然启动并从线程池中获取线程并消耗资源,或者它是否首先检查集合中是否有项目。

如果它不检查,为了避免这种情况,我在这段代码中考虑:

if(paramIeCollection.count > 0)
{
    //run Parallel.Foreach
}

所以问题是,在调用 Parallel.ForEach 之前检查集合是否有项目是一个好习惯,还是不需要?

说实话它确实会检查。但是,如果您浏览源代码,就会发现一些其他 制衡 它会在弄清楚之前进行。

如果您有 处理器指令 OCD,像 if(list.count > 0) 这样的简单事先检查可能会为您节省大量 IL。然而在现实世界中它不会有太大的不同

System.Threading.Tasks Reference Source

例如,对于简单的 IEnumerable 重载,您可以通过 E.g

跟随源代码
public static ParallelLoopResult ForEach<TSource>(IEnumerable<TSource> source, Action<TSource> body)

来电ForEachWorker

private static ParallelLoopResult ForEachWorker<TSource, TLocal>(
        IEnumerable<TSource> source,
        ParallelOptions parallelOptions,
        Action<TSource> body,
        Action<TSource, ParallelLoopState> bodyWithState,
        Action<TSource, ParallelLoopState, long> bodyWithStateAndIndex,
        Func<TSource, ParallelLoopState, TLocal, TLocal> bodyWithStateAndLocal,
        Func<TSource, ParallelLoopState, long, TLocal, TLocal> bodyWithEverything,
        Func<TLocal> localInit, Action<TLocal> localFinally)

内联呼叫

// This is an honest-to-goodness IEnumerable.  Wrap it in a Partitioner and defer to our
        // ForEach(Partitioner) logic.
return PartitionerForEachWorker<TSource, TLocal>(Partitioner.Create(source), parallelOptions, body, bodyWithState,
            bodyWithStateAndIndex, bodyWithStateAndLocal, bodyWithEverything, localInit, localFinally);

PartitionerForEachWorker

// Main worker method for Parallel.ForEach() calls w/ Partitioners.
private static ParallelLoopResult PartitionerForEachWorker<TSource, TLocal>(
        Partitioner<TSource> source, // Might be OrderablePartitioner
        ParallelOptions parallelOptions,
        Action<TSource> simpleBody,
        Action<TSource, ParallelLoopState> bodyWithState,
        Action<TSource, ParallelLoopState, long> bodyWithStateAndIndex,
        Func<TSource, ParallelLoopState, TLocal, TLocal> bodyWithStateAndLocal,
        Func<TSource, ParallelLoopState, long, TLocal, TLocal> bodyWithEverything,
        Func<TLocal> localInit,
        Action<TLocal> localFinally)

哪个最终进行检查

while (myPartition.MoveNext())

Parallel.ForEach 将在内部调用 IEnumerator.MoveNext(),这将 return false 用于空集合,因此不会完成任何工作。
虽然直接检查集合是否为空会更快,但差异可以忽略不计。