理解异步/同步与非阻塞/阻塞

异步/同步与非阻塞/阻塞其实是两回事。操作系统内核处理I/O只有两种方式:阻塞与非阻塞。

  • 阻塞I/O调用之后需要等待系统内核完成所有操作后,调用才能结束。阻塞I/O造成CPU等待,浪费了CPU的处理能力。
  • 非阻塞I/O调用之后会立即返回,CPU的时间片可以用来处理其他事务。立即返回造成的结果是,返回时并没有数据,因为完整数据需要I/O操作完成才能获取,因此需要轮询。

    轮询方式有许多种,一个比较好的方式是*nux下的epoll轮询(图片来自书籍):
    《深入浅出Node.js》学习笔记(2)异步I/O
    系统将对任何设备的I/O操作抽象为对文件的I/O。进入轮询时,应用层检查文件描述符上的事件状态,如果没有I/O事件,将会进行休眠,直到事件发生唤醒。这种事件通知机制使得CPU不必花费大量计算用于重复轮询,提高了性能。

遗憾的是,非阻塞I/O是系统内核层面的实现,对于应用程序而言,依然是一种同步。另外,不同系统内核对非阻塞I/O的支持度不一,因此Node使用了线程池模型来实现非阻塞异步I/O。

《深入浅出Node.js》学习笔记(2)异步I/O
当主线程需要进行I/O操作时,在线程池创建I/O线程,I/O线程以非阻塞或阻塞I/O的方式向系统内核获取数据。数据获取完成后,通过线程通信异步通知主线程,这样就实现了跨平台的异步I/O。

事件循环

Node进程开启后,会创建一个类似while(true)的循环:
《深入浅出Node.js》学习笔记(2)异步I/O
从图中可以看到这个机制类似于android中Looper的设计。每次循环都会检查是否存在事件需要处理,如果有,则取出一个事件,并检查事件是否有相关联的回调。如果有回调,则执行回调。当不存在需要处理的事件时,该循环退出。另一方面,事件源的设计遵循观察者模式。文件I/O、网络请求调用时,会产生一个特定类型的事件,被对应的观察者接收。事件循环检查这些观察者,每次”消费”一个事件。

相关文章:

  • 2021-09-11
  • 2022-02-24
  • 2022-12-23
  • 2022-01-31
  • 2020-01-10
  • 2021-12-20
  • 2021-07-17
  • 2021-11-09
猜你喜欢
  • 2021-04-30
  • 2021-08-09
  • 2021-04-21
  • 2021-08-29
  • 2022-12-23
  • 2021-06-26
  • 2021-07-15
相关资源
相似解决方案