繁体   English   中英

Java多线程 - 更少或更少线程做更多的线程?

[英]Java Multithreading - More Threads That Do Less, or Fewer Threads that Do More?

编辑 :这个问题可能也适用于其他语言 - 它背后的整体理论似乎主要是语言不可知。 但是,由于这将在JVM中运行,我确信JVM开销/线程与其他环境之间存在差异。

编辑2 :为了更好地澄清,我想主要的问题是哪个更好的可扩展性:拥有更小的线程,可以更快地返回,以便为其他工作负载处理其他工作块,或者尝试快速获得单个工作负载尽可能? 工作负载是顺序的,在这种情况下,多线程无助于加速单个工作单元; 它更希望提高整个系统的吞吐量(感谢Uri引导我澄清)。

我正在研究一个正在取代现有系统的系统; 当前的系统负载很重,因此我们已经知道更换需要具有高度可扩展性。 它与几个外部流程进行通信,例如电子邮件,其他服务,数据库等,我已经计划将其多线程化以帮助扩展。 我以前曾经在多线程应用程序上工作,没有任何高性能/可伸缩性要求,所以我在获得绝对最大的并发性方面没有太多经验。

我的问题是在线程之间划分工作的最佳方法是什么? 我正在看两个不同的版本,一个为整个工作流创建一个线程,另一个为每个步骤创建一个线程,继续前一个步骤(在一个新的/不同的线程中)步骤完成 - 可能使用NodeJS样式的回调系统,但不是非常关注直接实现细节。

我不太了解多线程的细节 - 例如上下文切换之类的东西 - 所以我不知道多个线程的开销是否会淹没每个线程中的执行时间。 一方面,与多线程相比,单线程模型似乎对于单个工作流来说是最快的; 但是,它也会占用整个工作流程的单个线程,而多个线程会更短,并且会更快地返回池中(我想,至少)。

希望潜在的概念很容易理解; 这是一个人为的伪代码示例:

// Single-thread approach
foo();
bar();
baz();

要么:

// Multiple Thread approach
Thread.run(foo);
when foo.isDone()
    Thread.run(bar);
    when bar.isDone()
         Thread.run(baz);

更新 :完全忘了。 我正在考虑多线程方法的原因是(可能是错误的)认为,由于线程的执行时间较短,因此它们可用于整个工作负载的其他实例。 如果每个操作花费5秒钟,那么单线程版本将锁定一个线程15秒; 多线程版本将锁定单个线程5秒,然后它可以用于另一个进程。

有任何想法吗? 如果在互联网上有类似的东西,我甚至喜欢一个链接 - 我想不出如何搜索这个(我周一责备这个,但明天可能会是一样的)。

多线程不是一颗银弹。 这意味着结束。 在进行任何更改之前,您需要问自己瓶颈在哪里,以及您真正想要并行化的内容。 我不确定如果没有更多信息,我们可以在这里给出好的建议。

如果foo,bar和baz是管道的一部分,那么通过使用多个线程,您不一定会改善单个序列的整体延迟。

您可以做的是通过让不同输入部件上的多个管道执行并行工作来提高吞吐量,方法是让后续项目通过管道,而先前的项目被阻塞(例如,I / O) 。 例如,如果特定输入的bar()被阻塞并等待通知,则可能您可以对另一个输入执行计算量大的操作,或者将CPU资源用于foo()。 一个特别重要的问题是,任何外部依赖项是否都充当有限的共享资源。 例如,如果一个线程正在访问系统X,另一个线程是否会受到影响?

如果你想分割和克服你的问题,将线程分成很小的部分,通过管道运行每个部分,然后等待所有部分准备好,线程也非常有效。 您正在查看的工作流程是否可行?

如果你需要先做foo ,然后做bar ,然后做baz ,你应该有一个线程按顺序执行这些步骤。 这很简单,显而易见。

使用装配线方法最好的最常见情况是将代码保留在缓存中比将数据保存在缓存中更重要。 在这种情况下,让一个反复执行foo线程可以将此步骤的代码保留在缓存中,保持分支预测信息,等等。 但是,当您将foo的结果传递给执行bar的线程时,您将有数据缓存未命中。

这更复杂,只有在你有充分的理由认为它会更好的时候才应该尝试。

使用单个线程完成整个工作流程。

划分工作流程无法改善一项工作的完成时间:由于工作流程的各个部分必须按顺序完成,因此一次只能有一个线程处理该工作。 然而,分解这些阶段可能会延迟一件作品的完成时间,因为可能已经拿起一件作品的最后一部分的处理器可能会取而代之的是另一件作品的第一部分。

将阶段分解为多个线程也不太可能改善完成所有工作的时间,相对于在一个线程中执行所有阶段,因为最终您仍然必须执行所有工作的所有阶段。

这是一个例子。 如果你有200个这样的工作,每个需要三个5秒的阶段,并且说两个线程在两个处理器上运行,将整个工作流保持在一个线程中会在15秒后产生前两个结果。 获得所有结果需要1500秒,但您一次只需要两个工作的工作记忆。 如果你打破了阶段,那么获得你的第一个结果可能需要花费超过15秒的时间,如果你仍然希望在1500秒内获得所有结果,你可能需要并行处理所有200件工作的内存。

在大多数情况下,将顺序阶段分解为不同的线程没有效率优势,并且可能存在实质性的缺点。 线程通常仅在您可以使用它们并行工作时才有用,而您的工作阶段似乎并非如此。

然而,将阶段分解为单独的线程存在巨大的缺点。 缺点是您现在需要编写管理阶段的多线程代码。 在这样的代码中编写错误非常容易,并且在生产部署之前很难捕获这样的错误。

避免此类错误的方法是根据您的要求尽可能简化线程代码。 在您的工作阶段,最简单的线程代码是完全没有。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM