在nodejs中,主要批评者基于其单线程事件循环模型。
nodejs的最大缺点是无法在应用程序中执行CPU密集型任务。出于演示目的,让我们以while循环为例(这也许类似于db函数返回十万条记录,然后在nodejs中处理这些记录。)
while(1){
x++
}
此类代码将阻塞主堆栈,因此在事件队列中等待的所有其他任务将永远无法获得执行的机会。 (在网络应用程序中,新用户将无法连接到该应用程序)。
但是,人们可以使用cluster
之类的模块来利用多核系统并部分解决上述问题。群集模块允许创建一个由多个独立进程组成的小型网络,这些网络可以共享服务器端口,这使Node.js应用程序可以访问服务器的全部功能。 (但是,使用Cluster的最大缺点之一是无法在应用程序代码中维护状态)。
但是,如果服务器负载过多,很可能再次遇到相同的情况(如上所述)。
当我开始学习Go语言并了解它的体系结构和goroutines时,我认为它可能会解决由于nodejs的单线程事件循环模型而引起的问题。而且这可能会避免上述CPU密集型任务的情况,直到我遇到了这个有趣的代码,该代码阻止了所有GO应用程序并且没有任何反应,就像nodejs中的while循环一样。
func main() {
var x int
threads := runtime.GOMAXPROCS(0)
for i := 0; i < threads; i++ {
go func() {
for { x++ }
}()
}
time.Sleep(time.Second)
fmt.Println("x =", x)
}
//or perhaps even if we use some number that is just greater than the threads.
因此,问题是,如果我有一个负载密集型的应用程序,并且还会有很多CPU密集型任务,那么我可能会陷入上述情况。 (其中db返回大量行,然后应用程序需要处理和修改这些行中的某些内容)。不会阻止传入的用户,其他所有任务也会被阻止吗?
那么,如何解决上述问题?
P.S
也许,我提到的用例没有多大意义? :)
答案 0 :(得分:3)
当前(Go 1.11和更早版本)您所谓的 紧密循环确实会阻塞代码。 发生这种情况仅仅是因为当前Go编译器 插入执行“抢占检查”的代码(« 到调度程序,以便它运行另一个goroutine?»)仅在 它编译的函数的序言(几乎,但让我们别离题)。 如果您的循环未调用任何函数,则不会进行抢占检查 将被制成。
Go开发人员很清楚这一点 和are working on eventually alleviating this issue。
不过,请注意,您所指控的问题是在 最真实的场景:执行时间长的代码 运行CPU密集型工作,而无需调用任何函数 很稀有,介于两者之间。
在某些情况下,您确实拥有这样的代码并且您拥有 检测到,这确实使其他goroutine饿了 (请让我强调:您已经通过剖析检测到 而不是仅仅想出“它一定很慢”),您可以 应用几种技术来解决这个问题:
runtime.Gosched()
的呼叫
您长时间运行的CPU密集型代码。
这将强制放弃对另一个goroutine的控制
而实际上并没有暂停调用程序goroutine(因此它将
请尽快重新运行。N
“工人goroutines”中; N
比runtime.GOMAXPROCS
小
或引发后者,以便您有N
个额外的线程。