Node.js的事件轮询Event Loop原理解释
事件轮询主要是针对事件队列进行轮询,事件生产者将事件排队放入队列中,队列另外一端有一个线程称为事件消费者会不断查询队列中是否有事件,如果有事件,就立即会执行,为了防止执行过程中有堵塞操作影响当前线程读取队列,事件消费者线程会委托一个线程池专门执行这些堵塞操作。
Javascript前端和Node.js的机制类似这个事件轮询模型,有的人认为Node.js是单线程,也就是事件消费者是单线程不断轮询,如果有堵塞操作怎么办,不是堵塞了当前单线程的执行吗?
其实Node.js底层也有一个线程池,线程池专门用来执行各种堵塞操作,这样不会影响单线程这个主线程进行队列中事件轮询和一些任务执行,线程池操作完以后,又会作为事件生产者将操作结果放入同一个队列中。
总之,一个事件轮询Event Loop需要三个组件:
- 事件队列Event Queue,属于FIFO模型,一端推入事件数据,另外一端拉出事件数据,两端只通过这个队列通讯,属于一种异步的松耦合。
- 队列的读取轮询线程,事件的消费者,Event Loop的主角。
- 单独线程池Thread Pool,专门用来执行长任务,重任务,干繁重体力活的。
在Node.js中,因为只有一个单线程不断地轮回查询队列中是否有事件,对于数据库 文件系统等I/O操作,包括HTTP请求等等这些容易堵塞等待的操作,如果也是在这个单线程中实现,肯定会堵塞影响其他工作任务的执行,Javascript/Node.js会委托给底层的线程池执行,并会告诉线程池一个回调函数,这样单线程继续执行其他事情,当这些堵塞操作完成后,其结果与提供的回调函数一起再放入队列中,当单线程从队列中不断读取事件,读取到这些堵塞的操作结果后,会将这些操作结果作为回调函数的输入参数,然后激活运行回调函数。
请注意,Node.js的这个单线程不只是负责读取队列事件,还会执行运行回调函数,这是它区别于多线程模式的一个主要特点,多线程模式下,单线程只负责读取队列事件,不再做其他事情,会委托其他线程做其他事情,特别是多核的情况下,一个CPU核负责读取队列事件,一个CPU核负责执行激活的任务,这种方式最适合很耗费CPU计算的任务。反过来,Node..js的执行激活任务也就是回调函数中的任务还是在负责轮询的单线程中执行,这就注定了它不能执行CPU繁重的任务,比如JSON转换为其他数据格式等等,这些任务会影响事件轮询的效率。
来源:https://www.jdon.com/idea/nodejs/event-loop.html