【问题标题】:Multi threading which would be the best to use? (Threadpool or threads)多线程哪个最好用? (线程池或线程)
【发布时间】:2011-07-08 18:34:10
【问题描述】:

希望这是一个比我之前的问题更好的问题。我有一个 .exe,我将传递不同的参数(文件路径),然后它将接收并解析。所以我会有一个循环,循环遍历列表中的文件路径并将它们传递给这个 .exe 文件。

为了提高效率,我想将执行分散到多个核心上,我认为你可以通过线程来完成。

我的问题是,我应该使用线程池还是多线程来异步运行这个.exe?

另外,取决于你们认为哪一个是最好的,如果你能指出我想要做的一些信息的教程。谢谢!

编辑: 我需要将 .exe 的执行次数限制为每个核心执行一次。这是最有效的,因为如果我要解析 100,000 个文件,我不能只启动 100000 个进程。因此,我使用线程将一次执行次数限制为每个内核一次执行。如果有其他方法(除了线程)来确定处理器是否没有被执行,或者 .exe 是否已完成,请解释。 但如果没有其他方法,我的最后一个问题是如何使用线程调用 parse 方法,然后在该线程不再使用时回调?

第二次更新(非常重要):

我仔细阅读了每个人告诉我的内容,发现了一个我认为无关紧要但我忽略的关键元素。所以我使用的是 GUI,我不希望它被锁定。这就是我想使用线程的原因。我现在的主要问题是,如何从线程发回信息以便知道执行何时结束?

【问题讨论】:

  • 指定您的操作系统、语言等
  • 您对文件的处理是否非常密集?如果不是,那么您可能会受到 I/O 限制,在这种情况下,线程很可能会使事情变得更慢而不是更快。

标签: c# multithreading multicore


【解决方案1】:

正如我在回答您上一个问题时所说,我认为您不了解进程和线程之间的区别。流程非常“繁重”(*);每个进程可以包含许多线程。如果您从父进程生成新进程,则该父进程不需要创建新线程;每个进程都有自己的线程集合。

如果所有工作都在同一个进程中完成,则仅在父进程中创建线程。

把线程想象成一个工人,一个进程想象成一个包含一个或多个工人的建筑物。

一种策略是“建造一栋建筑,并在其中安置十名工人,每个工人都做一些工作”。你得到了构建一个进程和十个线程的费用。

如果您的策略是“建造一座建筑物。然后让该建筑物中的一名工人命令建造一千多座建筑物,每座建筑物中都有一名工人按照他们的要求进行建造”,那么您将获得建造 1001 座建筑物的费用并雇用1001名工人。

想要采取的策略是“建造一座建筑物。在那栋建筑物中雇用 1000 名工人。然后指示每个工人建造一座建筑物,然后让一名工人去做真正的工作。”创建一个唯一的工作是创建一个进程然后创建一个线程的线程是没有意义的!您有 1001 座建筑物和 2001 名工人,其中一半立即闲置但仍需支付报酬。

查看您的具体问题:关键问题是“瓶颈在哪里?”只有当性能问题是处理器上的性能受到限制时,才产生新进程或新线程。如果您的解析器的性能不是取决于您可以多快解析文件,而是取决于您可以多快将其从磁盘中取出,那么并行化它会使事情变得更糟更糟。您将有大量系统资源用于同时在同一个磁盘控制器上进行所有锤击,并且随着更多负载堆积在磁盘控制器上,磁盘控制器将变得更慢。

更新:

我需要将 .exe 的执行次数限制为每个核心执行一次。这是最有效的,因为如果我要解析 100,000 个文件,我不能只启动 100000 个进程。因此,我使用线程将一次执行次数限制为每个内核一次执行。如果有其他方法(除了线程)可以查明处理器是否没有被执行,或者 .exe 是否已完成,请解释

这似乎是一种非常复杂的方法。假设您有 n 个处理器。据我了解,您提出的策略是启动 n 个线程,然后让每个线程启动一个进程,并且您知道,由于操作系统可能为每个 CPU 调度一个线程,不知何故处理器还会神奇地将每个新进程中的新线程调度到不同的 CPU 上?

这似乎是一个曲折的推理链,取决于操作系统的实现细节。这是疯狂。 如果您想设置特定进程的处理器亲和性,只需set the processor affinity on the process不要用线程做这种疯狂的事情,希望它能成功。

我说,如果您希望运行的可执行文件不超过 n 个实例,每个处理器一个,则根本不要乱用线程。相反,只需让一个线程处于循环中,constantly monitoring what processes are running。如果运行的可执行文件的副本少于 n 个,则生成另一个并将其处理器亲和性设置为您最喜欢的 CPU。如果有 n 个或更多的可执行文件副本正在运行,请进入睡眠状态一秒钟(或一分钟,或任何有意义的),当您醒来时,再次检查。继续这样做,直到你完成。这似乎是一种更简单的方法。


(*) 线程也很重,但比进程轻。

【讨论】:

  • 请在我编辑时重新检查我的问题,以进一步解释我的困境。很抱歉第一次没有充分解释。
  • @Joey:听起来你做的比你需要的要困难得多。如果您想设置进程的处理器亲和性,只需设置它。你为什么要尝试用线程来做这些乱七八糟的事情?
  • 快速问题:线程也有处理器亲和性吗?如果我每个核心启动 1 个线程,它们是否被限制在不同的核心上是否重要?
  • @configurator:是的;设置进程的处理器亲和性基本上是说“默认情况下,将此进程中的每个线程都关联到该处理器”。回复:你的第二个问题,我不确定。我不是这方面的专家,目前还不是。
【解决方案2】:

我会自发地将您的文件路径推送到 线程安全 队列中,然后启动多个线程(例如每个内核一个)。每个线程将重复从队列中弹出一个项目并相应地处理它。当队列为空时,工作就完成了。

实施建议(回答cmets中的一些问题):


队列:

在 C# 中,您可以查看 Queue ClassQueue.Synchronized Method 以了解队列的实现:

“这种类型的公共静态(在 Visual Basic 中为共享)成员是线程安全的。不保证任何实例成员都是线程安全的。 为了保证 Queue 的线程安全,所有操作都必须通过 Synchronized 方法返回的包装器来完成。 通过集合枚举本质上不是线程安全的过程。即使一个集合被同步,其他线程仍然可以修改该集合,这会导致枚举器抛出异常。为保证枚举过程中的线程安全,可以在整个枚举过程中锁定集合,也可以捕获其他线程更改导致的异常。”


线程:

对于线程部分,我想msdn threading tutorial 中的任何示例都可以(教程有点旧,但应该是有效的)。不需要担心同步线程,因为它们可以彼此独立工作。上面的队列是他们应该需要访问的唯一公共资源(因此队列的线程安全很重要)。


启动外部进程(.exe):

以下代码是从How to wait for a shelled application to finish by using Visual C# 借用(和调整)的。您需要根据自己的需要进行编辑,但作为初学者:

//How to Wait for a Shelled Process to Finish
//Create a new process info structure.
ProcessStartInfo pInfo = new ProcessStartInfo();
//Set the file name member of the process info structure.
pInfo.FileName = "mypath\myfile.exe";
//Start the process.
Process p = Process.Start(pInfo);
//Wait for the process to end.
p.WaitForExit();

伪代码:

Main thread;
   Create thread safe queue
   Populate the queue with all the file paths
   Create child threads and wait for them to finish

      Child threads:
         While queue is not empty  << this section is critical, not more then one  
            pop file from queue    << thread can check and pop at the time

            start external exe
                wait for it....
            end external exe 

         end while
      Child thread exits

   Main thread waits for all child threads to finish
Program finishes.

【讨论】:

  • "C#" 在标题中。能举个例子吗?
  • 好吧,线程如何知道何时抓取另一个?当线程完成解析时,我将如何构建这个线程系统(你可以在 sudo 代码中完成)以弹出另一个路径?
  • 好吧,他们需要将文件路径从队列中取出,然后将其作为参数提供给 exe。这并没有改变什么吗?
  • @JoeyG:不,应该没问题。
  • 大卫,您不需要检查线程何时完成处理文件,将其分配给您从队列中弹出的新文件路径吗?我该怎么做?这就是我遇到的主要问题
【解决方案3】:

this question for how to find out the number of cores

然后使用 Parallel.ForEachParallelOptions 并将 MaxDegreeOfParallelism 设置为核心数。

Parallel.ForEach(args, new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount }, (element) => Console.WriteLine(element));

【讨论】:

    【解决方案4】:

    如果您的目标是 .Net 4 框架,Parallel.For 或 Parallel.Foreach 非常有用。如果这些不符合您的要求,我发现 Task.Factory 也很有用且易于使用。

    【讨论】:

      【解决方案5】:

      要回答您修改后的问题,您需要流程。您只需要创建正确数量的运行 exe 的进程。不要担心将它们强制到特定的核心上。 Windows 会自动执行此操作。

      如何做到这一点:

      您想确定机器上的内核数。您可能只是知道它并对其进行硬编码,或者您可能想使用类似System.Environment.ProcessorCount 的东西。

      创建一个List&lt;Process&gt; 对象。

      然后您想使用System.Diagnostics.Process.Start 启动那么多进程。返回值将是一个进程对象,您需要将其添加到列表中。

      现在重复以下操作,直到完成:

      致电Thread.Sleep 稍等片刻。也许一分钟左右。

      循环遍历列表中的每个Process,但请务必使用for 循环而不是foreach 循环。对于每个进程,调用Refresh(),然后检查每个进程的'HasExited'属性,如果是,则使用Process.Start创建一个新进程,并将列表中已退出的进程替换为新创建的进程。

      【讨论】:

        【解决方案6】:

        如果您要启动 .exe,那么您别无选择。您将在一个单独的进程中异步运行它。对于启动的程序,我建议您使用单线程并保留您启动的进程列表。

        【讨论】:

          【解决方案7】:

          每个启动的 exe 都将在其自己的进程中发生。您不需要使用线程池或多个线程;操作系统管理进程(因为它们是进程而不是线程,所以它们非常独立;完全独立的内存空间等)。

          【讨论】:

            猜你喜欢
            • 2022-11-02
            • 2011-11-23
            • 1970-01-01
            • 2010-11-15
            • 1970-01-01
            • 2011-09-23
            • 2016-05-21
            • 2014-12-02
            • 2023-03-12
            相关资源
            最近更新 更多