node的进程与线程

node的进程与线程

Node.js的单线程指的是主线程是“单线程”

线程

1、每个Node.js进程只有一个主线程在执行程序代码,形成一个执行栈(execution context stack)。
2、主线程之外,还维护了一个"事件队列"(Event queue)。当用户的网络请求或者其它的异步操作到来时,node都会把它放到Event Queue之中,此时并不会立即执行它,代码也不会被阻塞,继续往下走,直到主线程代码执行完毕。
3、主线程代码执行完毕完成后,然后通过Event Loop,也就是事件循环机制,开始到Event Queue的开头取出第一个事件,从线程池中分配一个线程去执行这个事件,接下来继续取出第二个事件,再从线程池中分配一个线程去执行,然后第三个,第四个。主线程不断的检查事件队列中是否有未执行的事件,直到事件队列中所有事件都执行完了,此后每当有新的事件加入到事件队列中,都会通知主线程按顺序取出交EventLoop处理。当有事件执行完毕后,会通知主线程,主线程执行回调,线程归还给线程池。
4、主线程不断重复上面的第三步。

总结:node 的I/O操作非真实的阻塞型I/O而是将I/O扔进线程池然后等有事件执行完毕后,执行回调,线程归还给线程池。

node事件循环实现

Libuv是一个基于事件驱动的跨平台抽象层,封装了不同操作系统一些底层特性,对外提供统一的API。
libuv中的高效队列是用c语言, 只使用宏定义封装而成,
Node.js采用V8作为js的解析引擎,而I/O处理方面使用了自己设计的libuv,事件循环机制也是它里面的实现。

int uv_run(uv_loop_t* loop, uv_run_mode mode) {  int timeout;  int r;  int ran_pending;//首先检查我们的loop还是否活着//活着的意思代表loop中是否有异步任务//如果没有直接就结束
  r = uv__loop_alive(loop);  if (!r)
    uv__update_time(loop);//传说中的事件循环,你没看错了啊!就是一个大while
  while (r != 0 && loop->stop_flag == 0) { //更新事件阶段
    uv__update_time(loop); //处理timer回调
    uv__run_timers(loop); //处理异步任务回调 
    ran_pending = uv__run_pending(loop);//没什么用的阶段
    uv__run_idle(loop);
    uv__run_prepare(loop);    //这里值得注意了
    //从这里到后面的uv__io_poll都是非常的不好懂的
    //先记住timeout是一个时间
    //uv_backend_timeout计算完毕后,传递给uv__io_poll
    //如果timeout = 0,则uv__io_poll会直接跳过
    timeout = 0;    if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)
      timeout = uv_backend_timeout(loop);

    uv__io_poll(loop, timeout);    //就是跑setImmediate
    uv__run_check(loop);    //关闭文件描述符等操作
    uv__run_closing_handles(loop);    if (mode == UV_RUN_ONCE) {      /* UV_RUN_ONCE implies forward progress: at least one callback must have
       * been invoked when it returns. uv__io_poll() can return without doing
       * I/O (meaning: no callbacks) when its timeout expires - which means we
       * have pending timers that satisfy the forward progress constraint.
       *
       * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from
       * the check.
       */
      uv__update_time(loop);
      uv__run_timers(loop);
    }

    r = uv__loop_alive(loop);    if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)      break;
  }  /* The if statement lets gcc compile it to a conditional store. Avoids
   * dirtying a cache line.
   */
  if (loop->stop_flag != 0)    loop->stop_flag = 0;  return r;
}

事件循环就是一个大while而已

小结:

1、Nodejs与操作系统交互,我们在 Javascript中调用的方法,最终都会通过 process.binding 传递到 C/C++ 层面,最终由他们来执行真正的操作。Node.js 即这样与操作系统进行互动。
2、nodejs所谓的单线程,只是主线程是单线程,所有的网络请求或者异步任务都交给了内部的线程池去实现,本身只负责不断的往返调度,由事件循环不断驱动事件执行。
3、Nodejs之所以单线程可以处理高并发的原因,得益于libuv层的事件循环机制,和底层线程池实现。
4、Event loop就是主线程从主线程的事件队列里面不停循环的读取事件,驱动了所有的异步回调函数的执行,Event loop总共7个阶段,每个阶段都有一个任务队列,当所有阶段被顺序执行一次后,event loop 完成了一个 tick。

死锁

定义:死锁就是两个线程同时占用两个资源,但又在彼此等待对方释放锁。
在这里插入图片描述

同源任务

任务执行

1、任务队列又分为macro-task(宏任务)与micro-task(微任务),在最新标准中,它们被分别称为task与jobs。
2、macro-task大概包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。
3、micro-task大概包括: process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性)
4、setTimeout/Promise等我们称之为任务源。而进入任务队列的是他们指定的具体执行任务。
5、来自不同任务源的任务会进入到不同的任务队列。其中setTimeout与setInterval是同源的。
6、事件循环的顺序,决定了JavaScript代码的执行顺序。它从script(整体代码)开始第一次循环。之后全局上下文进入函数调用栈。直到调用栈清空(只剩全局),然后执行所有的micro-task。当所有可执行的micro-task执行完毕之后。循环再次从macro-task开始,找到其中一个任务队列执行完毕,然后再执行所有的micro-task,这样一直循环下去。
7、其中每一个任务的执行,无论是macro-task还是micro-task,都是借助函数调用栈来完成。

Browser端严格遵循第六条,执行完一个宏任务就会去检查微任务队列是否有需要执行的微任务,即使微任务内嵌套微任务,也会将嵌套的微任务执行完毕后(这点上nodejs与browser是相同的,对应的就是清空微任务的队列),再去宏任务队列执行下一个宏任务;

nodejs端则会将同源的任务放在一起执行,如果涉及到同源宏任务的嵌套,仍会将同源任务放在一起,但是内部的任务会放在下一次事件循环时执行。

代码验证:
console.log(1);
    setTimeout(() => {
        console.log(2)
        new Promise((resolve) => {
            console.log(6);
            resolve(7);
        }).then((num) => {
            console.log(num);
        })
 
    });
    setTimeout(() => {
        console.log(3);
           new Promise((resolve) => {
            console.log(9);
            resolve(10);
        }).then((num) => {
            console.log(num);
        })
        setTimeout(()=>{
        	console.log(8);
        })
    })
    new Promise((resolve) => {
        console.log(4);
        resolve(5)
    }).then((num) => {
        console.log(num);
        new Promise((resolve)=>{
        	console.log(11);
        	resolve(12);
        }).then((num)=>{
        	console.log(num);
        })
    })

node 11以前
在这里插入图片描述
node 14
在这里插入图片描述

node 的 child-process

进程

定义:进程是计算机分配资源调度的基本单元,每一个应用程序启动会开启一个进程。分配固定的资源以及一个进程ID并且进程是独立的,进程之间可以通过IPC通信

线程

定义:线程是计算机计算的最小单元,进程可以有多个线程,但线程只属于一个进程。线程可共享进程的部分资源,node是单线程应用(一个进程一个线程)但node通过创建多进程来进行实现多线程。(以提高CPU利用率)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值