Parallel.ForEach没有启动新线程

Parallel.ForEach没有旋转新线程

大家好,我们有一个非常密集的IO操作,我们使用Microsoft的.NET Framework并行扩展中的Parallel.ForEach编写。 我们需要删除大量文件,并且我们将要删除的文件表示为列表列表。 每个嵌套列表中都有1000条消息,我们有50条这样的列表。 这里的问题是,当我查看后面的日志时,我只看到在Parallel.ForEach块内执行一个线程。

这是代码的样子:

List<List> expiredMessagesLists = GetNestedListOfMessages(); foreach (List subList in expiredMessagesLists) { Parallel.ForEach(subList, msg => { try { Logger.LogEvent(TraceEventType.Information, "Purging Message {0} on Thread {1}", msg.MessageID, msg.ExtensionID, Thread.CurrentThread.Name); DeleteMessageFiles(msg); } catch (Exception ex) { Logger.LogException(TraceEventType.Error, ex); } }); } 

我用一个更简单的数据结构编写了一些示例代码,没有IO逻辑,我可以在Parallel.ForEach块中看到几个不同的线程。 我们在上面的代码中使用Parallel.ForEach做错了吗? 它可能是绊倒它的列表列表,还是IO操作存在某种线程限制?

有几种可能性。

首先,在大多数情况下, Parallel.ForEach不会生成新线程。 它使用.NET 4 ThreadPool(所有TPL都有),并将重用ThreadPool线程。

话虽这么说,Parallel.ForEach使用基于传递给它的List大小的分区策略。 我的第一个猜测是你的“外部”列表有很多消息,但内部列表只有一个Message实例,所以ForEach分区只使用一个线程。 使用一个元素, Parallel足够智能,只需使用主线程,而不是将工作旋转到后台线程。

通常,在这种情况下,最好并行化外部循环,而不是内部循环。 这通常可以为您提供更好的性能(因为您将拥有更大的工作项),尽管如果不了解循环大小加上工作单元的大小,很难知道。 您也可以将内循环和外循环并行化,但如果没有分析,很难说出什么是最佳选择。

另一种可能性:

尝试使用[Thread.ManagedThreadId][1]而不是Thread.CurrentThread.Name进行日志记录。 由于Parallel使用ThreadPool线程,因此“Name”在多个线程中通常是相同的。 当你实际上使用多个线程时,你可能认为你只使用一个线程….

您的代码的基础假设是可以并行删除文件。 我不是说它不是(我不是这方面的专家),但如果对大多数硬件来说根本不可能,我也不会感到惊讶。 毕竟,当您执行此操作时,您将使用物理对象(您的硬盘)执行操作。

假设您有一个类Person ,其方法名为RaiseArm() 。 你总是可以尝试在100个不同的线程上射击RaiseArm() ,但是RaiseArm()只能筹集两个……

就像我说的,我可能是错的。 这只是我的怀疑。