node 事件循环的 一点理解
发布于 9 年前 作者 wdragon1983 9131 次浏览 最后一次编辑是 8 年前 来自 分享

不知道理解的对不对。请指点。

    最近因为项目需求。刚刚开始看了一些node。看了大师写的深入浅出node。里面提到node的事件循环。   js是单线程,对于阻塞操作,js会封装参数和回调函数,交给底层去处理。 也就是 io线程池。线程池处理完毕会放到一个类似队列里面。然后node的事件循环线程 会去获取这个队列的数据,执行回调。
	 但是书上面没说事件循环是什么线程在执行的。我也在想这个问题。因为node的js端是单线程的。不需要考虑同步问题,那么得出结论是这个回调必然是node的主线程在执行。 既然是主线程在执行,那么必然主线程就是事件循环线程。而事件循环是个类似while(true)的结构。这样的话主线程就不能做其他事情,就是不停的执行事件循环,这也就是node的回调机制的核心。 这里面其实就是一个问题,node启动的时候,必然要把所有的非回调的代码走一遍,然后就不会再执行其他代码,其他代码必然是在回调函数里面执行。 
	 这个也就是我刚开始不了解的地方。可能很多人应该也不理解, 网上看了有人提问。说http应用的时候,如果主线程忙着去处理客户端连接,怎么还有时间去处理回调。这个其实就是理解的错误。因为处理连接这个不是js主线程代码处理的,主线程只是处理,连接之后的connect回调事件。
	 
	 以上是我对node事件循环的一点理解。不知道是否靠谱。请高人指点!
11 回复

我们写的所有 js 代码是同一个线程执行的,所以不用考虑线程安全。 事件循环while true是在另一个线程执行的,所以不会阻塞。 事实上负责调度的线程并不止一个,因为很多系统操作并没有异步实现,这些线程会帮我们完成异步的效果。

Sent from CNodejs for iOS

@klesh 你意思事件循环 不是在node 主线程执行的?

我不认为是这样,如果另外一个线程执行回调函数,而不是主线程,那么js就不是线程安全的了。2个线程(主线程,事件循环线程)执行何来安全。 佐证: setTimeout(function(){ console.log(“test”); },100); while(true){ console.log(“while”); }

这里面的setTimeout 是永远不会执行的。因为主线程一直在占用。 事件循环while(true)不会执行。

我是这样理解的,是否还有其他原因?

哪个是主哪个是次取决于你怎么看。 我的意思是我们交给 node.js 的所有 js 代码是跳在同一个线程上的(就叫它J)。然后有另一个线程(我们看不见的,就叫它E)负责调度这些代码执行,它维护一个事件队列(叫它Q),还有另一些线程配合完成调度(这些不重要)。 我们的J开始执行,它一行一行地走,执行到setTimeout时向E的事件队列Q插入这个函数(异步就是这么来的),然后继续往下走,到 while true 时它就阻塞了。如果 while 被 break 了。那么这个 J 执行完了。E才会去Q取出下一个元素,也就是刚才插入的函数去执行(当然这里还有100ms条件要满足)。当 Q 全部被执行,程序就退出了。 总之,我们写的 js 代码都是在同一个线程中执行的,而调度是另外的线程在干的。

@klesh 那么这个 J 执行完了。E才会去Q取出下一个元素,也就是刚才插入的函数去执行。。。 这里的问题是 E取出了Q去执行回调函数,是在哪一个线程里面执行的?是j还是E。 刚才j已经执行完了,这个j是不是就退出了,还是在等待状态。如果是E执行的话,那么E就 取代了J 成为主线程。 否则j如果依然存在,有什么意义?

E只负责调度(负责协调J和其它协助线程),J去执行我的写出来的js代码,这样才安全。J是不是同一条线程这个其实我也是猜的,但不同的话也没关系,E只要保证同一时间只有一个线程在执行JS代码也是安全的。具体是怎么实现的要去读源码了。 你应该是觉得我们写的js代码是主线程?但我觉得在整个过程中,E才能称之为主线程,它是负责去安排,统筹以及执行我们提交的js代码。

@klesh 我也是猜测,我觉得主线程就是J 或者 E 但是就一个。或者刚开始是j 执行完毕后,交给E。E就是主线程。 主线程就是while(true)的执行tick。执行tick其实就是 向各种io观察者 定时器观察者等 去拿数据 ,然后执行回调。 其实就是node 的 主线程,因为node执行js代码的就是一个线程。没有2个线程。 一种可能是 就是j线程。另一种可能是j执行完毕关闭,然后E线程接手。我认为应该不是j线程执行然后等待,然后E把要执行的代码交个j去执行。这个好像比较麻烦。

@wdragon1983 你原来的理解是对的,不要受别人影响,直接看源代码就是。 事件循环和js代码都是在主线程里执行的,闲得蛋疼才会开两个线程跑。

楼上有读过过源码,他的说法应该是对的。

Sent from CNodejs for iOS

@wanlingqing

建议看源代码。

nodejs不是单线程的,fs是线程池实现的,既然有其他线程,把事件循环部分叫做主线程也没什么不妥。

timer的确是小根堆实现的,但跟epoll无关。

你所谓的阻塞难道是只要不立即让出CPU都是阻塞?

所以tick的调用是发生在主线程运行结束后?如果主线程一直没有结束,那么事件循环是不是一直没有机会得以执行呢?

回到顶部