阻塞
线程在执行中如果遇到(I/O 操作)如磁盘读写或网络通信,通常要耗费较长的时间,这时操作系统会剥夺这个线程的 CPU 控制权,使其暂停执行,同时将资源让给其他的工作线程,这种线程调度方式称为 阻塞。当 I/O 操作完毕时,操作系统将这个线程的阻塞状态解除,恢复其对CPU的控制权,令其继续执行。这种 I/O 模式就是通常的同步式 I/O(Synchronous I/O)或阻塞式 I/O(Blocking I/O)。
非阻塞
非阻塞是这样定义的,当线程遇到 I/O 操作时,不会以阻塞的方式等待 I/O 操作的完成或数据的返回,而只是将 I/O 请求发送给操作系统,继续执行下一条语句。当操作系统完成 I/O 操作时,以事件的形式通知执行 I/O 操作的线程,线程会在特定时候处理这个事件。
对比阻塞与非阻塞
阻塞模式下,一个线程只能处理一项任务,要想提高吞吐量必须通过多线程。
非阻塞模式下,一个线程永远在执行计算操作,这个线程所使用的 CPU 核心利用率永远是 100%,I/O 以事件的方式通知。
在阻塞模式下,多线程往往能提高系统吞吐量,因为一个线程阻塞时还有其他线程在工作,多线程可以让 CPU 资源不被阻塞中的线程浪费。
而在非阻塞模式下,线程不会被 I/O 阻塞,永远在利用 CPU。多线程带来的好处仅仅是在多核 CPU 的情况下利用更多的核。
来看看《深入浅出Node.js》对异步I/O的解释,在操作系统中,程序运行的空间分为内核空间和用户空间。我们常常提起的异步I/O,其实质是用户空间中的程序不用依赖内核空间中的I/O操作实际完成,即可进行后续任务。
异步I/O优缺点
使用同步IO,它的优点是可以使程序调试方便,但是它的缺点也是明显的,程序的执行过程中如果入到一些耗时的IO操作,程序的执行都要等待该IO的完成,在这个等待的过程中,程序无法充分利用CPU,导致了CPU的闲置,为了充分利用CPU,和IO并行操作,常用的方法有2中:
(1)多线程单进程
多线程的设计之处就是为了在共享的程序空间中,实现并行处理任务,从而达到充分利用CPU的效果。
多线程缺点:
其一、执行时(线程切换)上下文交换的开销较大,一个线程大约需要2M的内存空间,占用资源较大。
其二、状态同步(锁)的问题,它也使得程序的编写和调用复杂化。
(2)单线程多进程
为了避免多线程造成的使用不便问题,有的语言选择了单线程保持调用简单化,采用启动多进程的方式来达到充分利用CPU和提升总体的并行处理能力。它的缺点在于业务逻辑复杂时(涉及多个I/O调用),因为业务逻辑不能分布到多个进程之间,事务处理时长要远远大于多线程模式。