计算 Parallel.ForEach 使用的线程数

Posted

技术标签:

【中文标题】计算 Parallel.ForEach 使用的线程数【英文标题】:Count number of threads used by Parallel.ForEach 【发布时间】:2018-04-30 02:40:57 【问题描述】:

如何确定在特定调用Parallel.ForEach(或Parallel.Invoke,或Parallel.For)期间使用的线程数

我知道如何限制最大线程数,例如

Parallel.ForEach(myList, 
                 new ParallelOptions  MaxDegreeOfParallelism = 4 ,
                 item =>  doStuff(item); );

我知道,除了当前线程之外,Task.Parallel 库使用一些启发式方法来确定在运行时使用的附加线程池线程的最佳数量;介于 0 和 MaxDegreeOfParallelism 之间的某个值。

我想知道实际使用了多少线程,用于记录目的:

Stopwatch watch = Stopwatch.StartNew();
Parallel.ForEach(myList, item =>  doStuff(item); );
trace.TraceInformation("Task finished in 0ms using 1 threads", 
       watch.ElapsedMilliseconds, NUM_THREADS_USED); 

我主要是出于好奇和增进理解而希望记录这些数据。它不必是 100% 可靠的,因为我不打算将它用于其他任何事情。

有没有办法获得这个数字,而不会造成重大的性能损失?

【问题讨论】:

【参考方案1】:

您可以使用(线程安全的)列表来存储已使用线程的 ID 并对其进行计数:

ConcurrentBag<int> threadIDs = new ConcurrentBag<int>();
Parallel.ForEach(myList, item =>  
    threadIDs.Add(Thread.CurrentThread.ManagedThreadId);
    doStuff(item); 
);

int usedThreads = threadIDs.Distinct().Count();

确实会影响性能(尤其是ConcurrentBag 的线程安全逻辑),但我不知道影响有多大。相对效果取决于doStuff 自己做了多少工作。如果那个方法只有几个命令,这个线程计数解决方案甚至可能改变使用的线程数。

【讨论】:

使用ConcurrentDictionary&lt;int, byte&gt;而不是ConcurrentBag可能会更好一点?【参考方案2】:

在您的 DoStuff 方法中,您可以像这样添加代码

    private void DoStuff(T item)
        
            Logger.Log($"Item item.ToString() was handled by thread # Thread.CurrentThread.ManagedThreadId");
            // your logic here
        

【讨论】:

【参考方案3】:

虽然我知道这是一个较老的问题,但我还是跟进了 Evk 的建议。也不确定性能影响,但您可以使用并发字典来跟踪线程 ID:

var threadIDs = new ConcurrentDictionary<int, int>();
Parallel.ForEach(myList, item =>  
    threadIDs.TryAdd(Thread.CurrentThread.ManagedThreadId, 0);
    doStuff(item); 
);

int usedThreads = threadIDs.Keys.Count();

【讨论】:

以上是关于计算 Parallel.ForEach 使用的线程数的主要内容,如果未能解决你的问题,请参考以下文章

何时使用 Parallel.ForEach,何时使用 PLINQ

何时使用 Parallel.ForEach,何时使用 PLINQ

强制终止由 Parallel.ForEach 生成的线程 [重复]

使用 Parallel.ForEach() 的 yield return 的线程安全

为什么线程比Parallel.Foreach更快打开OracleConnection?

在 parallel.ForEach 循环中获取线程 ID