c# - Parallel.ForEach 没有启动新线程

标签 c# .net multithreading parallel-extensions

Parallel.ForEach 不启动新线程

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

代码如下:

List<List<Message>> expiredMessagesLists = GetNestedListOfMessages();
foreach (List<Message> 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 block 中执行的多个不同线程。我们在上面的代码中对 Parallel.ForEach 做错了什么吗?可能是列表的列表绊倒了它,还是 IO 操作存在某种线程限制?

最佳答案

有几种可能性。

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

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

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

另一种可能性:

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

关于c# - Parallel.ForEach 没有启动新线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1861548/

相关文章:

c# - 缓存 System.Console 输出

java.lang.IllegalStateException : Not on FX application thread; currentThread = Thread-4

java - Java 在 32 位机器上可以创建的线程数

c# - 流利的 NHibernate : SetBatchSize vs. AdoNetBatchSize

c# - 如何在关闭前等待串口清空

c# - 未满18岁如何不注册

java - 如何将线程添加到 ArrayList

c# - 缓存和线程安全

c# - 嵌套循环的代码效率 c#

.net - Unity IOC Buildup vs Resolve?