gpt4 book ai didi

multithreading - 哪个更适合 node.js 上的并发任务?纤维?网络 worker ?或线程?

转载 作者:行者123 更新时间:2023-12-03 04:42:19 26 4
gpt4 key购买 nike

前段时间我偶然发现了 node.js 并且非常喜欢它。但很快我发现它严重缺乏执行 CPU 密集型任务的能力。所以,我开始使用谷歌搜索并得到了这些解决问题的答案:Fibers、Webworkers 和 Threads (thread-a-gogo)。现在使用哪一个是一个困惑,其中一个肯定需要使用 - 毕竟拥有一个只擅长 IO 而没有别的服务器的目的是什么?需要建议!

更新:

我在想一种方法来晚了;只需要对此提出建议。现在,我想到的是:让我们有一些线程(使用 thread_a_gogo 或 webworkers)。现在,当我们需要更多时,我们可以创造更多。但是在创建过程中会有一些限制。 (不是系统暗示的,但可能是因为开销)。现在,当我们超过限制时,我们可以 fork 一个新 Node ,并开始在它上面创建线程。这样,它可以一直持续到我们达到某个极限(毕竟,进程也有很大的开销)。当达到这个限制时,我们开始排队任务。每当一个线程空闲时,它就会被分配一个新任务。这样,它才能顺利进行。

所以,这就是我的想法。这个主意好不好?我对所有这些过程和线程的东西都有些陌生,所以没有任何专业知识。请分享您的意见。

谢谢。 :)

最佳答案

Node 有一个完全不同的范式,一旦它被正确捕获,就更容易看到这种不同的解决问题的方式。在 Node 应用程序 (1) 中永远不需要多个线程,因为你有不同的方式来做同样的事情。您创建多个进程;但它与例如 Apache Web Server 的 Prefork mpm 的工作方式非常不同。

现在,让我们认为我们只有一个 CPU 内核,我们将开发一个应用程序(以 Node 的方式)来做一些工作。我们的工作是处理一个大文件,它的内容逐字节地运行。对于我们的软件来说,最好的方法是从文件的开头开始工作,逐字节地跟踪到结尾。

-- 嘿,哈桑,我想你要么是我祖父时代的新手,要么是非常老的学校!!!为什么不创建一些线程并使其更快?

-- 哦,我们只有一个 CPU 内核。

——那又怎样?创建一些线程,让它更快!

——它不是那样工作的。如果我创建线程,我会让它变慢。因为我将为系统增加大量开销以在线程之间切换,试图给它们足够的时间,并在我的进程中尝试在这些线程之间进行通信。除了所有这些事实之外,我还必须考虑如何将单个工作分成可以并行完成的多个部分。

——好吧好吧,我看你很穷。让我们用我的电脑吧,它有 32 个内核!

——哇,亲爱的 friend ,你真棒,非常感谢。我很感激!

然后我们回去工作。多亏了这位富有的 friend ,现在我们有了 32 个 cpu 内核。我们必须遵守的规则刚刚改变。现在我们想利用我们所获得的所有这些财富。

要使用多个内核,我们需要找到一种方法将我们的工作分成可以并行处理的部分。如果不是 Node,我们会为此使用线程; 32 个线程,每个 CPU 核心一个。但是,由于我们有 Node,我们将创建 32 个 Node 进程。

线程可以很好地替代 Node 进程,甚至可能是更好的方法;但仅限于特定类型的工作,其中工作已经定义并且我们可以完全控制如何处理它。除此之外,对于工作以我们无法控制且希望尽快回答的方式来自外部的所有其他类型的问题,Node 的方式无疑是优越的。

-- 嘿,Hasan,你还在单线程工作吗?你怎么了,伙计?我刚刚给了你你想要的。你再也没有借口了。创建线程,使其运行得更快。

-- 我已将工作分成几部分,每个流程都将并行处理其中一个部分。

-- 你为什么不创建线程?

-- 抱歉,我认为它不可用。如果你愿意,你可以带上你的电脑吗?

-- 不好吧,我很酷,我只是不明白你为什么不使用线程?

——谢谢你的电脑。 :) 我已经将工作分成几部分,并创建了并行处理这些部分的流程。所有 CPU 内核都将得到充分利用。我可以用线程而不是进程来做到这一点;但是 Node 有这种方式,我的老板 Parth Thakkar 希望我使用 Node。

-- 好的,如果您需要另一台计算机,请告诉我。 :p

如果我创建 33 个进程,而不是 32 个,操作系统的调度程序将暂停一个线程,启动另一个线程,在一些周期后暂停它,再次启动另一个线程......这是不必要的开销。我不想要它。事实上,在具有 32 个内核的系统上,我什至不想创建正好 32 个进程,31 个可能更好。因为不仅仅是我的应用程序可以在这个系统上运行。为其他东西留一点空间可能会很好,特别是如果我们有 32 个房间。

我相信我们现在在充分利用处理器来处理 CPU 密集型任务的问题上是一致的。

-- 嗯,哈桑,对不起,我有点 mock 你。我相信我现在更了解你了。但是我仍然需要解释一些事情:运行数百个线程的所有嗡嗡声是什么?我到处都读到线程的创建速度比 fork 进程快得多?你 fork 进程而不是线程,并且你认为它是你使用 Node 获得的最高值。那么 Node 不适合做这种工作吗?

——别担心,我也很酷。每个人都说这些话,所以我想我已经习惯听到它们了。

——所以? Node 不适合这个?

-- Node 非常适合这个,尽管线程也可以。至于线程/进程创建开销;在你重复很多的事情上,每一毫秒都很重要。但是,我只创建了 32 个进程,而且会花费很少的时间。它只会发生一次。它不会有任何区别。

-- 那我什么时候要创建数千个线程呢?

-- 您永远不想创建数千个线程。但是,在执行来自外部的工作的系统上,例如处理 HTTP 请求的 Web 服务器;如果您为每个请求使用一个线程,您将创建很多线程,其中很多。

-- Node 是不同的,但是?对吗?

——对,没错。这就是 Node 真正闪耀的地方。就像线程比进程轻得多,函数调用也比线程轻得多。 Node 调用函数,而不是创建线程。在 Web 服务器的示例中,每个传入请求都会导致函数调用。

——嗯,有意思;但是如果您不使用多个线程,则只能同时运行一个函数。当大量请求同时到达 Web 服务器时,这如何工作?

-- 您对函数如何运行完全正确,一次一个,而不是两个并行。我的意思是在单个进程中,一次只运行一个范围的代码。 OS 调度程序不会来暂停这个函数并切换到另一个函数,除非它暂停进程以给另一个进程而不是我们进程中的另一个线程留出时间。 (2)

-- 那么一个进程如何一次处理2个请求呢?

-- 只要我们的系统有足够的资源(RAM、网络等),一个进程可以一次处理数以万计的请求。这些功能的运行方式是关键的区别。

——嗯,我现在应该兴奋吗?

-- 也许 :) Node 在队列上运行一个循环。在这个队列中是我们的工作,即我们开始处理传入请求的调用。这里最重要的一点是我们设计函数运行的方式。我们没有开始处理请求并让调用者等待我们完成工作,而是在完成可接受的工作量后迅速结束我们的功能。当我们需要等待另一个组件做一些工作并返回一个值时,我们不再等待,而是简单地完成我们的函数,将其余的工作添加到队列中。

——听起来太复杂了?

-- 不不,我可能听起来很复杂;但系统本身非常简单,而且非常有意义。

现在我想停止引用这两个开发人员之间的对话,并在最后一个关于这些功能如何工作的快速示例之后完成我的回答。

通过这种方式,我们正在做 OS Scheduler 通常会做的事情。我们在某个时候暂停我们的工作,让其他函数调用(如多线程环境中的其他线程)运行,直到再次轮到我们。这比将工作留给操作系统调度程序要好得多,操作系统调度程序试图为系统上的每个线程提供时间。我们比 OS Scheduler 更了解我们正在做的事情,并且我们应该停止时停止。

下面是一个简单的例子,我们打开一个文件并读取它来对数据做一些工作。

同步方式:

Open File
Repeat This:
Read Some
Do the work

异步方式:
Open File and Do this when it is ready: // Our function returns
Repeat this:
Read Some and when it is ready: // Returns again
Do some work

如您所见,我们的函数要求系统打开一个文件,而不是等待它被打开。它通过在文件准备好后提供后续步骤来完成自己。当我们返回时,Node 会在队列上运行其他函数调用。运行完所有函数后,事件循环移动到下一个回合......

综上所述,Node 与多线程开发有着完全不同的范式;但这并不意味着它缺乏东西。对于同步作业(我们可以决定处理的顺序和方式),它与多线程并行一样有效。对于来自外部的工作,例如对服务器的请求,它简直是优越的。

(1) 除非您使用其他语言(如 C/C++)构建库,在这种情况下您仍然不会创建用于划分作业的线程。对于这种工作,您有两个线程,其中一个将继续与 Node 通信,而另一个执行实际工作。

(2) 事实上,由于我在第一个脚注中提到的相同原因,每个 Node 进程都有多个线程。然而,这不像 1000 个线程在做类似的工作。这些额外的线程用于诸如接受 IO 事件和处理进程间消息之类的事情。

更新(作为对评论中一个好问题的回复)

@Mark,感谢您的 build 性批评。在 Node 的范式中,除非队列中的所有其他调用都设计为一个接一个地运行,否则您永远不应该拥有处理时间过长的函数。对于计算量很大的任务,如果我们完整地查看图片,我们会发现这不是“我们应该使用线程还是进程?”的问题。但问题是“我们如何以均衡的方式将这些任务划分为子任务,以便我们可以在系统上使用多个 CPU 内核并行运行它们?”假设我们将在具有 8 个内核的系统上处理 400 个视频文件。如果我们想一次处理一个文件,那么我们需要一个系统来处理同一个文件的不同部分,在这种情况下,多线程单进程系统可能更容易构建,甚至更高效。我们仍然可以通过运行多个进程并在需要状态共享/通信时在它们之间传递消息来使用 Node。正如我之前所说,Node 的多进程方法与此类任务中的多线程方法一样;但不止于此。同样,正如我之前所说,Node 的亮点是当我们将这些任务作为来自多个来源的系统输入时,因为与每个连接的线程或每个连接的进程相比,在 Node 中同时保持多个连接要轻得多系统。

至于 setTimeout(...,0)来电;有时可能需要在耗时的任务期间暂停,以允许队列中的调用有自己的处理份额。以不同的方式划分任务可以使您免于这些;但是,这并不是真正的黑客,它只是事件队列的工作方式。另外,使用 process.nextTick自从您使用 setTimeout 以来,这个目标要好得多。 , 计算和检查通过的时间是必要的,而 process.nextTick就是我们真正想要的:“嘿任务,回到队列的末尾,你已经用完了你的份额!”

关于multithreading - 哪个更适合 node.js 上的并发任务?纤维?网络 worker ?或线程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10773564/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com