这几天一直在看node相关。说node是单线程又高并发的框架。
但是实际上nodejs只在应用层属于单线程,底层其实是通过libuv维护了一个堵塞I/0调用的线程池。(这句话啥意思呢?我也不懂,但是libuv是c语言写的,我就知道这点哈哈哈)
再说一下这个event loop。
一直以为event loop是js的专属,但是经过查询资料发现:
eventloop 是一种程序结构,是实现异步的一种机制
1。所有任务都在主线程上执行,形成一个执行栈(execution context stack)。
2。主线程之外,还存在一个"任务队列"(task queue)。系统把异步任务放到"任务队列"之中,然后主线程继续执行后续的任务。
3。一旦"执行栈"中的所有任务执行完毕,系统就会读取"任务队列"。如果这个时候,异步任务已经结束了等待状态,就会从"任务队列"进入执行栈,恢复执行。
4。主线程不断重复上面的第三步。
所以说,js还是node都是换汤不换药被?
但是其实还是不一样的。
js为啥单线程这个就老生常谈了,用户在页面里操作dom,他只能是单线程的啊。如果是多线程,那么用户很多操作dom的动作,要优先于哪一个线程呢?
既然js是单线程的,当他在处理任务的时候就会有eventloop任务队列。
首先,有俩任务
1.同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。
2.不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
那说回node的单线程,最开始不是说他只在应用层是单线程的,底层是有一个线程池的~那我就考一下大佬的文字描述:
事件轮询主要是针对事件队列进行轮询,事件生产者将事件排队放入队列中,队列另外一端有一个线程称为事件消费者会不断查询队列中是否有事件,如果有事件,就立即会执行,为了防止执行过程中有堵塞操作影响当前线程读取队列,事件消费者线程会委托一个线程池专门执行这些堵塞操作。
Javascript前端和Node.js的机制类似这个事件轮询模型,有的人认为Node.js是单线程,也就是事件消费者是单线程不断轮询,如果有堵塞操作怎么办,不是堵塞了当前单线程的执行吗?
其实Node.js底层也有一个线程池,线程池专门用来执行各种堵塞操作,这样不会影响单线程这个主线程进行队列中事件轮询和一些任务执行,线程池操作完以后,又会作为事件生产者将操作结果放入同一个队列中。
Node.js也是单线程的Event Loop,但是它的运行机制不同于浏览器环境。
根据上图,Node.js的运行机制如下。
V8引擎解析JavaScript脚本。
解析后的代码,调用Node API。
libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎。
V8引擎再将结果返回给用户。
我们可以看到node.js的核心实际上是libuv这个库。这个库是c写的,它可以使用多线程技术,而我们的Javascript应用是单线程的。
Node.js不是用多个线程为每个请求执行工作的,相反而是它把所有工作添加到一个事件队列中,然后有一个单独线程,来循环提取队列中的事件。事件循环线程抓取事件队列中最上面的条目,执行它,然后抓取下一个条目。
在Node.js中,因为只有一个单线程不断地轮询队列中是否有事件,对于数据库文件系统等I/O操作,包括HTTP请求等等这些容易堵塞等待的操作,如果也是在这个单线程中实现,肯定会堵塞影响其他工作任务的执行,Javascript/Node.js会委托给底层的线程池执行,并会告诉线程池一个回调函数,这样单线程继续执行其他事情,当这些堵塞操作完成后,其结果与提供的回调函数一起再放入队列中,当单线程从队列中不断读取事件,读取到这些堵塞的操作结果后,会将这些操作结果作为回调函数的输入参数,然后激活运行回调函数。