多线程哪个最好用? (Threadpool或线程)

时间:2011-07-08 18:34:11

标签: c# multithreading multicore

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

为了提高效率,我希望将执行分散到多个核心,我认为你通过线程进行了这些操作。

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

另外,根据你们认为哪一个是最好的,如果你能指出一个教程,它将有一些关于我想做什么的信息。谢谢!

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

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

我经历了每个人都告诉我的事情,并找到了一个我遗漏的关键因素,我觉得无所谓。所以我使用的是GUI,我不希望它被锁定。这就是我想使用线程的原因。我现在的主要问题是,如何从线程发回信息,以便我知道执行何时结束?

7 个答案:

答案 0 :(得分:11)

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

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

将线程视为工作者,将流程视为包含一个或多个工作者的构建。

一个策略是“建造一个单独的建筑物并用十个工人填充它们,每个工人做一些工作”。你得到了构建一个进程和十个线程的费用。

如果你的策略是“建造一座建筑物。然后让那个建筑物中的一个工人命令建造另外一千多个建筑物,每个建筑物中都有一个工人进行招标”,那么你将获得建造1001座建筑物的费用雇用1001名工人。

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

看看你的具体问题:关键问题是“瓶颈在哪里?”当性能问题是perf在处理器上被门控时,产生新进程或新线程只会有所帮助。如果你的解析器的性能不是根据你解析文件的速度来决定的,而是取决于你从磁盘上获得它的速度有多快,那么并行化就会让事情变得更糟,更糟糕。你将拥有大量的系统资源用于同时在同一个磁盘控制器上进行锤击,并且随着更多的负载堆积,磁盘控制器将变慢。

UPDATE:

  

我需要将.exe的执行次数限制为一次执行PER CORE。这是最有效的,因为如果我解析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个或更多副本,请进入休眠状态一秒钟(或一分钟,或任何有意义的事情),当您醒来时,请再次检查。继续这样做,直到你完成。这似乎是一种更容易的方法。


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

答案 1 :(得分:3)

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

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


<强>队列:

在C#中,你可以查看Queue ClassQueue.Synchronized Method来实现队列:

“此类型的公共静态(在Visual Basic中为Shared)成员是线程安全的。不保证所有实例成员都是线程安全的。 为了保证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.

答案 2 :(得分:2)

请参阅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));

答案 3 :(得分:1)

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

答案 4 :(得分:1)

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

如何做到这一点:

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

创建一个List<Process>对象。

然后,您希望使用System.Diagnostics.Process.Start启动许多进程。返回值将是一个进程对象,您需要将其添加到List。

现在重复以下内容直至完成:

致电Thread.Sleep等待一段时间。也许一分钟左右。

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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