多线程 vs. 多实例 - 该选择哪个?

13
这两种情况之间会有很大的差别吗?
1. 一个应用程序实例创建100个线程来处理一些作业。 2. 相同应用程序的10个实例创建10个线程来处理作业(总共100个)。
在这两种情况下,线程数量都是相同的。是否其中一个方案具有性能或其他类型的改进?
引用:“Instance”是例如控制台应用程序,因此在第二种情况下,将运行10个控制台应用程序。每个应用程序都有自己的文件夹。

5
除非你在使用拥有很多处理器的Cray计算机,否则请勿创建100个同时活动的线程。 - 3Dave
你的进程需要通信吗? - Jimmy
对于之前错误的答案(已删除),我深表歉意。我测试了上述场景,发现单进程多线程应用程序实际上比多进程应用程序更快。我会尝试自己理解原因。 - Jimmy
感谢您的帮助。只是为了澄清要求:所有线程都是独立的,执行相同的任务(将数据发送到FTP服务器,但每个线程发送到不同的服务器),不需要线程同步,每个线程都有自己的数据。 从性能角度来看,我理解第一种方法更好。但是第二种方法可以更好地控制进程,在某些情况下也可能会有所帮助。 - Yaplex
5个回答

8
一个线程使用的资源比一个进程少,理论上来说选项1会更好。然而,你可能不会注意到两者之间有太大的差异,因为100个单独的线程或进程同时运行并争夺相同的操作系统资源几乎肯定会使系统崩溃。
我会选择选项3 - 包含一个相当小的线程池的一个进程。这样,一些作业将同时执行,其余作业将排队等待它们的轮到。如果要运行非常多的作业,这种方法也很容易扩展。
参见ThreadPool类,或更好的是,在其上面的许多高级抽象(例如任务库,甚至普通的异步委托)。

你说的 O/S 是指操作系统 OS 吗? - Vicky Dev
1
@VickyDev 这是正确的。 - Christian Hayter

3
选项2至少存在以下开销:
  • 更多的进程对象
  • 更多每个进程的静态内存
  • 更多CLR和JIT编译代码的实例
  • 上下文切换需要切换地址空间(非常昂贵)
  • 较少共享应用程序数据结构的机会
  • 需要跨进程通信。简单的方法调用变成IPC操作
  • 更多的工作量
  • 更多的错误机会(IPC通信、分叉炸弹等)
  • 更差的调试性能
  • 没有通过线程池进行内置负载平衡
  • 更难且容易出错的同步。内置内容较少且速度较慢

如果可以选择选项1,为什么要选择选项2呢?虽然有一些有效的原因,但这些原因相当特殊:

  • 您需要能够容忍任意的内存损坏。这不是正常情况(根本不是!)
  • 您需要能够杀死线程。在单个线程内部可靠地完成此操作是不可能的。但是你可以合作完成,这通常是更好的选择
  • 您的线程需要在不同的用户下运行等。这几乎从不发生。

通常情况下,进程越少越好。


你好,能否请您解释一下为什么进程间通信(IPC)更难调试?另外负载均衡方面有什么需要注意的吗? - user15463890

2
这取决于你正在做什么,但在大多数情况下,选项1将具有最佳性能,并且将是最易于使用的。
为了给您一个更完整的答案,我需要知道以下信息:
  • 这100个线程是否都执行相同的任务?
  • 这100个线程是否访问相同的数据?
  • 线程处理的任务是否会有自然的停机时间(等待另一个进程完成或资源变得可用)?
  • 线程处理的任务是否都尝试访问有限的资源(如硬盘或网络卡)?
  • 您的计算机可以同时处理多少个线程(例如,具有超线程技术的4核处理器可以处理8个线程,而没有超线程技术的4核处理器可以处理4个线程)?
  • 如果线程出现问题会发生什么? 进程崩溃,线程被重新启动吗?

如果这些线程都执行相同的任务,则将它们放在一起会使最终用户和后期开发人员更容易管理,因为所有内容都在一个地方。

如果所有的线程都访问同一数据,那么将它们保留在同一个进程中将允许您在这些线程之间共享该数据(尽管在更改数据时要注意竞态条件),并减少内存占用。您还可以组合这些线程来访问相同块的数据,这样所有东西都可以缓存在CPU上,减少内存延迟的影响,但我不建议尝试这样做。
由于许多答案都提供了如何实现您的项目的建议,因此知道每个线程是否设计为在运行时始终充分利用CPU,或者这些是在睡眠前执行少量工作的后台任务,将有助于我们为您的情况做出正确的建议。
了解进程将在哪种硬件上运行将有助于我们为您提供正确的建议。
如果一个线程失败,会发生什么?如果一个线程一天失败一次,用户是否需要干预,停止进程并重新启动它?如果是这样,那么在其自己的进程中运行每个线程将使您受益于只失去失败的进程。
Christian Hayter的第三种选择是有道理的,但在C#中并不总是相关。
如果您查看文档,它会说明:

操作系统ThreadId与托管线程没有固定关系,因为非托管主机可以控制托管和非托管线程之间的关系。具体而言,复杂的主机可以使用CLR Hosting API对同一操作系统线程调度许多托管线程,或将托管线程移动到不同的操作系统线程之间。

基本上,这意味着如果.NET框架认为这是一个好主意,它将池化您的线程。如果您的进程使用更多线程,则更有可能发生这种情况,而多线程进程之间的总线程数可能会保持相似。因此,我预计1个进程、100个线程的解决方案将使用比10个进程、每个进程10个线程(大约为10到40,但您必须检查)更少的总线程。

话虽如此,框架将会猜测,因此在某些情况下线程池会是更好的选择。请务必先阅读文档,因为有一些情况下不应使用线程池。关于池的快速教程可以在MSDN上找到。还有一个线程讨论何时应该使用线程池。
如果您提供更多信息,我会尝试给出更准确的答案。否则,在大多数情况下,选项1(可能还有选项3)是更好的选择。

1

是的,这将会有很大的差异。每种方法都有其优缺点:

  • 内存消耗。显然,每个进程都需要创建自己的地址空间等。
  • 通信/同步的简易性。虽然相对容易在线程之间进行通信/同步(可以使用关键段,这是最有效的方式之一),但在进程之间执行此操作会更加困难。在您的场景中,有10个进程和10个线程,您必须采用两种方式。
  • 崩溃。如果进程中的一个线程出了问题,整个进程都会崩溃。

我个人更喜欢一个进程/多线程的方法。但是,这真的取决于任务。


我测试了实际情况并发现多个进程都会变慢。你是正确的。我已删除了错误的答案。 - Jimmy

0

这是一个奇怪的问题,很难找到实际应用。但我想从性能角度来看,选项1可能更好。运行10个相同应用程序实例时,似乎会有更多的工作要做(清理、注册、打开控制台等)。

编辑* 因为使用选项1,您可以将工作排队,让线程队列处理负载。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接