Node.js有一个类似的技术(除了一切都发生在同一个线程上),但把所有等待的虚拟进程放在一个等待的队列中,直到他们收到来自阻塞资源(DB,URL)的响应,然后发送到被处理。
Node.js的缺点是它无法处理处理器密集型操作(for循环就是一个例子),并且正在运行的虚拟进程将花费所有时间直到完成而没有先发制人,这就是' s为什么Node.js在它被用于关键系统之前被明智地考虑,尽管它具有很高的并发可用性。
是的,Go会产生一个新的线程来处理阻塞的例程,但处理器密集型操作如何,它们被视为相同,还是遭受Node问题?
答案 0 :(得分:7)
是的,但是在实践中遇到比使用Node要困难得多,并且更容易从中恢复。节点是单线程的,除非您明确地编写多进程代码(这并不总是很容易,特别是如果您想要可移植)。 Go使用具有特定最大运行线程数的N:M调度(默认情况下等于逻辑CPU的数量,但是可调)。注意正在运行:正在等待阻塞操作的goroutine被“冻结”并且不算作占用正在运行的线程。
因此,如果你有一个单独的goroutine做一些CPU密集型的事情,它通常不会影响其他goroutine的运行能力,因为有许多其他线程可用来运行它们。如果你的goroutine的所有都被计算占用,那么在放弃CPU之前,其他的将无法运行。如果他们实际完成工作,这可能不会必然成为一个问题,因为所有的CPU都在做实际工作,但当然有时它可能处于延迟敏感的情况。
如果这是一个问题,我会想到三个解决方案:
在长时间计算期间使用runtime.Gosched
来控制处理器,允许其他goroutine运行。不需要做出其他改变;它只是一种使用协作调度程序的方法。 Gosched
可能会立即返回,或者可能会稍后返回。
使用工作池将并行CPU密集型工作量限制为小于GOMAXPROCS。 Go让这很容易。
同一枚硬币的翻转:将GOMAXPROCS提升到预期数量的并行计算任务之上。这可能是最糟糕的想法,并且会至少在一定程度上损害调度,但它仍然有效,并确保您有可用于处理事件的线程。
答案 1 :(得分:1)
Go使用cooperative multitasking。没有任何可以控制Go调度程序的东西的goroutine可以独占一个线程。