计算 Parallel.ForEach 使用的线程数

Count number of threads used by Parallel.ForEach

如何确定 Parallel.ForEach (or Parallel.Invoke, or 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 {0}ms using {1} threads", 
       watch.ElapsedMilliseconds, NUM_THREADS_USED); 

我主要是为了好奇而想记录这些数据,并增进我的理解。它不一定是 100% 可靠的,因为我不打算将它用于任何其他用途。

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

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

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

您可以使用(线程安全的)列表来存储已使用线程的 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 自身的工作量。如果该方法只有几个命令,这个线程计数解决方案甚至可以改变使用的线程数。

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

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

int usedThreads = threadIDs.Keys.Count();