2015-11-02 139 views
1

编辑:这个问题可能适用于其他语言 - 背后的整体理论似乎主要是语言不可知的。但是,因为这将在JVM中运行,所以我确定JVM开销/线程与其他环境之间存在差异。为了澄清一点,我想主要的问题是哪个更适合可伸缩性:具有更小的线程可以更快地返回,以便为其他工作负载处理其他工作块,或尝试获取尽可能快地完成单个工作量?在这种情况下,工作负载是连续的,多线程无助于加速单个工作单元;它更希望提高整个系统的吞吐量(感谢Uri带领我澄清)。Java多线程 - 更少的线程或更少的线程做更多的线程?

我正在开发一个替换现有系统的系统;目前的系统负载相当沉重,所以我们已经知道替换需要高度可扩展性。它与几个外部进程进行通信,如电子邮件,其他服务,数据库等,我已经计划使它成为多线程来帮助扩展。我以前在多线程应用程序上工作过,没有任何性能/可伸缩性需求的高点,所以我没有太多经验来获得绝对最大的并发性。

我的问题是在线程之间分配工作的最好方法是什么?我在看两个不同的版本,一个为完整的工作流创建一个线程,另一个为每个单独的步骤创建一个线程,继续下一步(在新的/不同的线程中)步骤完成 - 可能使用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); 

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

任何想法?如果在网页中有任何类似的内容,我甚至会喜欢这个链接 - 我想不出如何搜索这个(我为此责怪周一,但明天可能会是相同的)。

+6

如果你分拆新主题只有有父线程等待子线程完成,究竟是你获得? – Powerlord

+0

你是对的 - 我的意思是要覆盖,并完全跳过它。我会更新。 – MCory

+1

如果您希望事情运行得更快,请将同步而不是顺序任务分解为单独的线程。 –

回答

1

如果您需要先执行foo,然后再执行bar,然后执行baz,则应该有一个线程按顺序执行这些步骤中的每一个。这很简单,很明显。

最常见的情况下你是用流水线的方式最好是保持在高速缓存中的代码比将数据保持在高速缓存更重要的时候。在这种情况下,让一个反复执行foo的线程可以将此步骤的代码保留在缓存中,保持分支预测信息等等。但是,将foo的结果传递给bar的线程时,将会出现数据高速缓存未命中的情况。

这是比较复杂的,如果你有充分的理由认为这将更好地工作,只应尝试。

+0

我大体上同意,但我要指出的是,试图保持在高速缓存中的代码将复杂得多,问题之拟议例子只是每个工作分解成几部分组成。这个问题的例子仍然会随机地将这些部分分配给处理器,并且可能会增加而不是减少缓存未命中。 –

2

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

如果富,酒吧和巴兹是管道的一部分,你不一定要去通过使用多线程来提高单个序列的整体延迟。

你也许能够做的就是通过让管道在不同的输入块并行工作的多个执行,通过让后来的项目通过管道向行驶,而较早的项目被阻塞的东西(例如,增加你的吞吐量, I/O)。例如,如果特定输入的bar()被阻塞并等待通知,那么可能会对另一个输入执行计算量大的操作,或者将CPU资源投入到foo()中。一个特别重要的问题是,是否有任何外部依赖作为有限的共享资源。例如,如果一个线程正在访问系统X,另一个线程是否会受到影响?如果你想分而治之的问题

线程也是非常有效的 - 分裂的投入更小的部分,通过管道运行的每个部分,然后在所有的作品等着准备。这是可能的,你正在寻找哪种工作流程?

+0

我认为你对增加吞吐量的评论是我的目标。保持这个例子,如果3次调用每次需要5秒,那么至少需要15秒来处理单个输入。由于它是连续的,所以我无能为力。但会分解成多个线程使服务能够处理更多的请求比占用单个线程的全15秒更容易,或者它会更好,只是专注于获得个人项目尽快出? – MCory

+0

如果你有快速和慢速项的组合,那么它可能是有意义的以后“放下”大工作项目和“接他们回来了。”但是,如果所有的工作都是大致相同的工作量,那么一旦开始工作就可能没有意义。 –

+0

@Mcory:是否有任何步骤在等待外部事件时被阻止?并且对同一资源执行额外的并发请求会减慢或中断第一个请求? 如果你的计算是在CPU负载方面只是重,你的机器是不是真的闲着,所以额外的线程不会使吞吐量不一定更好。另一方面,如果请求正在等待通知,那么只要让每个输入通过不同线程的管道就足够了。 – Uri

0

对整个工作流使用单线程。

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

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

下面是一个例子。如果您有200个这样的工作,每个工作需要三个5秒的阶段,并且说两个线程在两个处理器上运行,那么将整个工作流保持在单个线程中会在15秒后得到前两个结果。获得所有结果需要1500秒,但您一次只需要工作记忆中的两项工作。如果分解了各个阶段,那么获得第一个结果可能需要15秒以上的时间,如果您仍想在1500秒内获得所有结果,则可能需要并行处理所有200个工作的内存。

在大多数情况下,有没有效率优势,打破了连续的阶段分成不同的线程,并有可能成为明显的缺点。线程通常只在您可以使用它们并行工作时才有用,这似乎并不适用于您的工作阶段。

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

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