当存在更多线程时,每个线程都会变慢

问题描述 投票:0回答:3

我有 16 个文件需要读取和处理数据。所有数据都是独立的。这是处理该数据的代码片段:

int threadCount = 1;
ConcurrentBag<SomeClass> items = new ConcurrentBag<SomeClass>();

Parallel.For(0, threadCount, i =>
{
     string[] itemsInfo = File.ReadAllLines("itemInfo" + i + ".txt");
     int index = 0;
     Console.WriteLine("Loading items...");
     List<SomeClass> threadItems = new List<SomeClass>();
     while (itemsInfo[index] != "<!---ITEMS END---!>")
     {
           SomeClass item = new SomeClass();
           index = item.FromString(itemsInfo, index);
           threadItems .Add(item);

           if (i == 0)
           {
              Console.WriteLine("Reading " + item.Id);
           }
     }

     foreach (SomeClass item in threadItems )
     {
          items .Add(item);
     }
});

有问题的部分是 while 循环。

SomeClass.FromString
只是进行一些字符串解析,它只依赖于字符串中的数据。

如果我运行如图所示的代码片段,线程计数为 1,则每秒大约处理 20 个项目。如果我将 threadCount 更改为 16(我使用的 CPU 确实有 16 个虚拟核心),它会减慢到大约每秒一项。

请注意,在这两种情况下,正在处理完全相同的文件。唯一的区别是,在第二种情况下,另外 15 个线程都在处理自己的文件。

当我测量速度时,我可以确认所有线程都在 while 循环内。我还可以确认当时 RAM 尚未填满。我还可以说,我观察到的情况适用于 1 到 16 之间的任意数量的线程,线程越多,第一个线程就越慢。

还能是什么?

c# multithreading performance parallel.for
3个回答
2
投票

每秒 16 x 1 个项目与每秒 1 x 20 个项目大致相同。添加线程并没有加快进程,但也没有减慢进程。您的硬盘(即使是 SSD)是瓶颈。它不可能跑得比它还快。为了证明我的观点,请尝试在运行之前读取所有线程的所有数据

Parallel.For
,然后测量使用 1 个线程和 16 个线程运行的性能。这次 16 个线程应该更快完成,因为最慢(且不可并行)的部分已从测量中剔除。

如果您有 16 个 SSD 驱动器,每个文件位于单独的驱动器上,则下一个瓶颈将是内存总线。只有其中之一,所有的数据都要经过它。


0
投票

您可以尝试几种方法。

  • 提前阅读所有文件。如果它们很大,您的内存将会受到压力,这可能会导致 GC 收集,从而降低整体速度。在这种情况下,
  • 流式传输文件,以避免内存压力。
  • foreach
    更改为
    threadItems.AddRange()
    。跨线程的循环可能会导致争用。
  • 使用线程调度程序调整并行化。

更激进的方法是在循环之外打开文件流并将每行管道传输到队列。这隔离了 IO。队列可以有多个消费者处理项目(受 CPU 限制)。 TPL 数据流 是一种更惯用的方法。

最后,基准测试、基准测试、基准测试。


0
投票

简单地添加更多线程来完成任务并不会自动提高速度。需要考虑正在执行的任务类型。在许多情况下,创建新线程的开销比在多个线程上执行工作的好处要昂贵。对于计算成本不是很高的操作来说,通常会出现这种情况。

并行化对于有瓶颈的操作也没有好处,在这种情况下可能是 PC 磁盘。在并行读取方面,HDD 通常比 SSD 慢,但这也取决于磁盘规格。

此外,您拥有 16 个虚拟核心而不是实际核心,这意味着并行工作不会提供您在具有多个物理核心的计算机上所期望的那么多好处。

您需要执行的操作可能不会通过并行化获得任何性能优势。

© www.soinside.com 2019 - 2024. All rights reserved.