【问题标题】:Parallel Programing with Threads线程并行编程
【发布时间】:2013-06-18 10:42:47
【问题描述】:

好的,我对我应该做什么和如何做有点困惑。我知道并行编程和线程的理论,但这是我的情况:

我们在给定文件夹中有许多日志文件。我们在数据库中读取这些日志文件。通常读取这些文件需要几个小时才能读取,因为我们以串行方法执行此操作,即我们遍历每个文件,然后为每个文件打开一个 SQL 事务并将日志插入数据库,然后读取另一个并执行相同操作。

现在,我正在考虑使用并行编程,这样我就可以消耗 CPU 的所有内核,但是我仍然不清楚是否对每个文件使用线程,这会对系统产生什么影响吗?我的意思是,如果我创建 30 个线程,那么它们会在单核上运行还是在 Parallel 上运行?我怎样才能同时使用它们?如果他们还没有这样做?

编辑:我使用的是单服务器,具有 10K 硬盘速度和 4 核 CPU,具有 4 GB RAM,没有网络操作,SQL Server 与 Windows 2008 作为操作系统在同一台机器上。 [如果有帮助,也可以更改操作系统 :)]。

编辑 2:我根据您的反馈进行了一些测试,这是我在具有 4 GB RAM 的 i3 四核 CPU 上找到的结果

  1. CPU 保持在 24-50% CPU1,CPU2 保持在 50% 以下,CPU3 保持在 75%,CPU4 保持在 0% 左右。是的,我打开了 Visual Studio、eamil 客户端和许多其他应用程序,但这告诉我应用程序没有使用所有内核,因为 CPU4 仍然是 0%;

  2. RAM 始终保持在 74% [测试前约为 50%],这就是我们设计读取的方式。所以,不用担心

  3. HDD 保持 READ/Write 或使用值保持低于 25% 甚至在正弦波中飙升至 25%,因为我们的 SQL 事务首先存储在内存中,然后在内存达到阈值时写入磁盘,再说一遍,

所以这里的所有资源都没有得到充分利用,因此我认为我可以分配工作以提高效率。又是你的想法。谢谢。

【问题讨论】:

  • 在更换硬件之前尝试增强软件,应该会少一些麻烦;)
  • 是的,我正在重写软件以提高速度 :) 因此这个问题。
  • 无意冒犯,但从你的问题来看,你似乎并不了解并行编程和线程的理论。
  • 好吧,svick 也许你是对的,我的知识有限,但你能分享一些想法,以便我也能提高我的理解吗?
  • @SumitGupta 尝试从头开始教授并行编程超出了单个 SO 问题的范围。这个话题太宽泛了。

标签: c# .net multithreading parallel-processing


【解决方案1】:

首先,您需要了解您的代码以及为什么它很慢。如果你在想“我的代码很慢并且使用一个 CPU,所以我让它使用所有 4 个 CPU,它会快 4 倍”,那么你很可能错了。

在以下情况下使用多线程是有意义的:

  1. 您的代码(或至少部分代码)受 CPU 限制。也就是说,它不会因为您的磁盘、网络连接或数据库服务器而变慢,而是因为您的 CPU 变慢。
  2. 或者您的代码有多个部分,每个部分使用不同的资源。例如。一部分从磁盘读取,另一部分转换数据,这需要大量 CPU,最后一部分将数据写入远程数据库。 (并行化这实际上并不需要多个线程,但它通常是最简单的方法。)

根据您的描述,您可能处于第二种情况。一个很好的解决方案是生产者消费者模式:阶段 1 线程从磁盘读取数据并将其放入队列中。阶段 2 线程从队列中获取数据,处理它们并将它们放入另一个队列。 Stage 3 线程从第二个队列中取出处理后的数据,并将它们保存到数据库中。

在 .Net 4.0 中,您将使用 BlockingCollection<T> 作为线程之间的队列。当我说“线程”时,我的意思是Task。在 .Net 4.5 中,您可以使用 TPL 数据流中的块而不是线程。

如果您这样做,那么您可以将执行速度提高三倍(如果每个阶段花费相同的时间)。如果第 2 阶段是最慢的部分,那么您可以通过为该阶段使用多个线程来获得另一个加速(因为它受 CPU 限制)。这同样适用于第 3 阶段,具体取决于您的网络连接和数据库。

【讨论】:

  • @SumitGupta 我认为获得一般统计数据是不够的。您应该衡量您的应用程序,以了解应用程序的哪个部分减慢了您的速度。
  • 好吧,我想我有了一些继续前进的想法,没有什么是真正的阻塞,因为应用程序正在全面利用资源,唯一的阻塞是软件设计,这就是问题的原因。我正在使用您建议的任务库,看看它是否真的加快了速度。再次感谢您的指导。
【解决方案2】:

这个问题没有明确的答案,你必须测试,因为正如我的 cmets 中提到的:

  • 如果瓶颈是磁盘 I/O,那么添加更多线程不会获得太多收益,甚至可能会降低性能,因为更多线程将争夺对磁盘的访问权

    1234563
  • 如果您可以执行更多磁盘和网络 I/O 并且 CPU 负载不高(很可能),那么您可以超额订阅(远)多于内核的线程:通常如果您的线程花费大量时间等待数据库

因此,您应该先配置文件,然后(或者如果您赶时间的话直接)测试不同的配置,但您很可能会遇到第三种情况。 :)

【讨论】:

    【解决方案3】:

    首先,您应该检查花费时间的原因。如果 CPU 确实是瓶颈,并行处理会有所帮助。也许是网络,更快的网络连接会有所帮助。也许购买更快的光盘会有所帮助。

    在考虑解决方案之前先找到问题。

    【讨论】:

    • 我正在从本地机器读取 10K 速度磁盘,客户端也准备好获取 SSD,但问题是串行过程中仅使用单核,并且我们通常有四核 CPU,所以我认为并行我们可以获得速度。
    • 您是否测量到 CPU 实际上已达到极限?您是否测量了将数据传输到数据库时使用的带宽?
    • 内核越多,如果瓶颈是 I/O,情况就会变得最糟糕。
    【解决方案4】:

    您的问题是没有使用所有 CPU,您的操作主要是 I/O(读取文件,发送数据到 DB)。

    使用线程/并行将使您的代码运行得更快,因为您同时处理许多文件。

    为了回答您的问题,框架/操作系统将优化在不同内核上运行您的代码。

    【讨论】:

    • 如果单线程消耗所有 I/O 带宽、磁盘总线带宽和网络带宽来访问数据库,不确定它会运行得更快。好吧,不太可能:)
    • 不太可能。另外 - 有几个 I/O,比如 dist 和 network。 1个线程可以占用1的全部带宽(如磁盘)但不能同时使用网络。多线程可以使用所有的 I/O(比如 1 个线程占用磁盘,1 个线程占用网络)
    • 如果一个线程将输入从磁盘流式传输到数据库,则它可以消耗两者的全部带宽。 :)
    【解决方案5】:

    它因机器而异,但一般来说,如果你有一个双核处理器并且你有 2 个线程,那么操作系统会将一个线程传递给一个内核,另一个线程传递给另一个内核。使用多少核心并不重要,重要的是您的方程式是否最快。如果您想使用并行编程,您需要一种以合乎逻辑的方式共享工作负载的方法。此外,您还需要考虑瓶颈实际发生在哪里。根据文件的大小,可能只是您读取/写入存储介质的最大速度需要这么长时间。作为测试,我建议您记录代码中消耗最多时间的位置。

    测试非串行方法是否对您有帮助的一种简单方法是按某种顺序对文件进行排序,将工作负载分配给同时执行相同工作的 2 个线程,看看是否有所不同。如果第二个线程对您没有帮助,那么我保证 30 个线程只会使其花费更长的时间,因为操作系统必须切换线程和第四个线程。

    【讨论】:

    • 我的问题是我是否应该使用并行编程或线程或两者兼而有之。就像如果我使用并行读取文件,这在逻辑上不相关,每个日志都有单独的日期时间戳的单独条目,首先读取哪个文件或何时读取无关紧要,然后它将读取两个到处理器,但仍然线程会有所帮助此外 ?似乎它更多的是测试用例,但总体思路是我正在寻找的。不过感谢您的回复。
    • 如果有很多 I/O,您可以考虑比内核多得多的线程。例如,当您从具有数十个线程的网络下载文件时,这是一个好主意,因为等待数据到来需要时间。
    • Sumit,如果你没有最大化你的读/写并且大部分时间都在以某种方式处理信息而不是读写它,那么理论上多个线程应该会有所帮助。我这样做是因为如果您正在阅读您的媒体所能达到的最大速度,那么多线程将无济于事。正如 Pragmateek 指出的那样,许多操作(例如从多个位置下载文件)当然是使用多个线程的好主意,因此如果一个位置滞后,除非绝对必要,否则它不会冻结或减慢整个操作。
    【解决方案6】:

    使用 .Net 4 中的最新结构进行并行编程,通常会为您管理线程...阅读getting started with parallel programming (与最近发生的情况几乎相同,如果你希望它是异步的,可以使用异步版本的函数)

    例如

    for (int i = 2; i < 20; i++)
    {
        var result = SumRootN(i);
        Console.WriteLine("root {0} : {1} ", i, result);
    }
    

    变成

    Parallel.For(2, 20, (i) =>
    {
        var result = SumRootN(i);
        Console.WriteLine("root {0} : {1} ", i, result);
    });
    

    编辑:也就是说,将密集的任务放入单独的线程中可能会更有效率/更快......但是手动使您的应用程序成为“多核”并拥有某些线程之类的东西在特定的核心上运行,这目前是不可能的,这一切都在后台管理......

    例如查看plinq.Net Parallel Extensions 并查看

    System.Diagnostics.Process.GetCurrentProcess().ProcessorAffinity = 4
    

    编辑2: 并行处理可以在具有多个线程的单核内完成。

    多核处理意味着分配这些线程以利用 CPU 中的多个内核。

    【讨论】:

    • 嗯,我知道语法谢谢。正如我提到的,我的问题是关于应用程序的概念设计:)。
    • 我最初的评论是对概念设计的看法,即 .Net 包含一些并行扩展,其中线程是为您管理的......阅读编辑
    • 多核并不总是并行化
    • 好的,感谢您的指导。我认为最好的办法是只做实验:)。