附录8 同步异步、阻塞非阻塞
同步异步、阻塞非阻塞、网络编程与并行编程
参考文献
0 问题概述
背后的思想
- 我觉得,同步异步,更像是思想,是一种哲学思想,而不是某种具体的技术或者方案,它在不同的环节,能够表现出不同的形式。
- 我觉得,在这个问题上,我过度思考了,太想把这两个次应用到不同的领域,因为在不同的领域都见过。通信就是通信,有很多种通信方式和机制。设备IO就是设备IO也有很多不同的实现方案,没有必要非得对应到同步和异步上。像,通信,就那几种机制,直到就行,干嘛非得划分为同步通信或者异步通信?
- 不应该以同步和异步来划分领域,而应该说明相关领域中的同步异步操作。这里只是在对各个领域中涉及到的同步异步思想进行整理。而非对并发编程、网络编程等进行分类。
需要回答以下问题
- 同步异步的机制原理有哪些?抽象的模型和方法,包括怎样的设计模式?
- 同步异步的应用场景有哪些?具体使用了怎样的机制?
- 同步异步的实现方式有哪些?具体到每种语言、操作系统、数据库、计算机网络,进行说明。
问题归化
针对该领域的问题,可以归化为三种场景:同步互斥问题,进程通信问题,设备IO问题。
- 同步互斥问题的目标是解决资源访问的问题,核心是某类公共资源。实现多个用户进程,对临界资源的访问,包括互斥访问和顺序访问,临界资源一般为共享内存或者某个数据结构。
- 进程通信问题的目标是解决实现多个进程、线程之间的数据交换问题,核心是交换数据。是多个用户进程线程之间,实现通信的方式。也包括网络通信问题
- 设备IO问题的目标是解决用户进程与内核进程之间的通信问题。设备IO中两种典型的例子是:磁盘读写、网络通信。
其中进程通信问题和设备IO问题可以规约为一种,例如网络通信,就可以看做网卡的IO问题和整体上的远程用户进程通信,当然,我觉得把这种东西抽象出来,将远程通信作为一种特例,是否能够更好的理解呢,它本身也可以看做一种特殊的情况。另外还有同步调用和异步调用的区别,侧重的是调用过程,而不是通信过程?
同步调用与异步调用
同步异步思想应用在函数调用上的一种体现。
同步调用链。我感觉,无论是单机还是网络通信,是直接调用还是并行通信,都存在一个同步调用链,当这个链路上的任何一个环节,变成异步,整个过程看起来就是异步的,能够感知到这个异步过程的部分,才会觉得是异步,例如服务器上虽然通过异步线程和事件响应机制处理,是异步的,但是客户端却感知不到,得到结果的过程还是同步的。
而其他部分如果包含了这个异步过程,看起来就是异步的,如果不包含,看起来就是同步的。还是例如网络通信,可以在主机的网卡通信过程中实现异步,直接返回一个信号,两方继续执行即可(没人会这么干吧,网络上异步代价太大了),也可以直接实现单个设备IO的异步,例如客户端用户进程继续执行,而内核进程等待返回结果。服务器启动额外的进程处理结果,异步的方式返回处理结果(服务器启动的多个用户进程之间的并行,通过消息通知主线程,结果得到了,然后返回给客户端消息)这里是局部的异步执行,但是整体上反而是同步的,其他线程处理好后,通知主线程,通过事件响应机制或者其他方式,告诉主线程,然后返回结果。
更准确的说,异步的过程实现了并发,同步的过程实现了顺序。无论是怎样通信,同步异步只是这个过程中的一部分,这条调用链的任何一部分都可以改成异步的,然后这里就可以实现并发处理。这也是并发的一部分,我应该从并发入手,从同步异步的角度,探讨问题,已经够深入了。
吐槽
为什么这个问题这么难以理解呢,因为几个名词之间的层次与关系太乱。同步与互斥可以对应,实现了进程同步。同步异步可以对应,实现了通信和IO。同步异步对应,还能实现并发。同步异步讲的既能是多线程,也能是单线程,同步调用?还能是同步通信。待会把这里搞明白。同步是一种机制,可以在资源互斥访问上,可以在并行通信上,可以在函数调用上,也可以在设备IO上,含义相似又不完全相同。虽然其本身几句话就能说清楚何为同步何为异步,但是另一方面,却很难将这两个概念对应到具体的应用场景和过程中,它的表现形式过于复杂。
1 进程同步里的——同步互斥
概述
针对两种制约关系,合作制约关系和互斥制约关系,需要通过同步机制实现。
- 直接制约关系(合作)。由于多个进程相互合作产生,使得进程有一定的先后执行关系。
- 间接制约关系(互斥)。由于多个进程资源共享产生,多个进程在同一时刻只有一个进程能进入临界区。
显然,同步是一种更为复杂的互斥,而互斥是一种特殊的同步。也就是说互斥是两个任务之间不可以同时运行,他们会相互排斥,必须等待一个线程运行完毕,另一个才能运行,而同步也是不能同时运行,但他是必须要安照某种次序来运行相应的线程(也是一种互斥)!因此互斥具有唯一性和排它性,但互斥并不限制任务的运行顺序,即任务是无序的,而同步的任务之间则有顺序关系。
2 进程通信里的——同步异步、阻塞非阻塞
同步异步
- 同步与异步关注的是数据通信机制。是两个进程之间的关系。
- 同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。
- 异步,当一个异步过程调用发出后,调用就直接返回,调用者不会立刻得到结果。被调用者通过状态、通知和回调函数来通知调用者。
阻塞非阻塞
- 阻塞和非阻塞关注的是程序在等待调用结果时的状态。是一个进程的本身的状态。
- 阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
- 非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。
3 设备IO里的——同步异步、阻塞非阻塞
nodejs中相关定义
- 同步式I/O(Synchronous I/O)或阻塞式I /O (Blocking I/O)。线程/进程在执行中如果遇到磁盘读写或网络通信(统称为I/O 操作),通常要耗费较长的时间,这时 操作系统会剥夺这个线程/进程的CPU 控制权,使其暂停执行,同时将资源让给其他的工作线程,这种线程调度方式称为阻塞 。当I/O 操作完毕时,操作系统将 这个线程的阻塞状态解除,恢复其对CPU的控制权,令其继续执行。
- 异步式 I/O (Asynchronous I/O)或非阻塞式I/O (Non-blocking I/O)。则针对所有I/O 操作不采用阻塞的策略。当线程 遇到I/O 操作时,不会以阻塞的方式等待I/O 操作的完成或数据的返回,而只是将I/O 请求发送给操作系统,继续执行下一条语句。当操作系统完成I /O 操作时,以事件的形式通知执行I/O 操作的线程,线程会在特定时候处理这个事件。为了处理异步I/O,线程必须有事件循环,不断地检查有没有未处 理的事件,依次予以处理。
- 并发方式。阻塞模式下,一个线程只能处理一项任务,要想提高吞吐量必须通过多线程。而非阻塞模式下,一个线程永远在执行计算操作,这个线程所使用的CPU 核心利用率永远是100%,I/O 以事件的方式通知。在阻塞模式下,多线程往往能提高系统吞吐量,因为一个线程阻塞时还有其他线程在工作,多线程可以让CPU 资源不被阻塞中的线程浪费。而在非阻塞模式下,线程不会被I/O 阻塞,永远在利用CPU。多线程带来的好处仅仅是在多核CPU 的情况下利用更多的核,而Node.js的单线程也能带来同样的好处。这就是为什么Node.js 使用了单线程、非阻塞的事件编程模式。
一种更深的理解——在看完五种IO模型之后的反思
- 同步和异步是针对应用程序和内核的交互而言的;
- 同步指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪。可以是阻塞等待也可以是非阻塞的轮询。
- 异步是指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知。
- 阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作函数的实现方式;
- 阻塞方式下读取或者写入函数将一直等待,
- 非阻塞方式下,读取或者写入函数会立即返回一个状态值。
4 进程通信和设备IO的关系
关于同步异步、阻塞非阻塞。交互方式,分别有两种应用。一种用在进程间的同步和通信上;一种用在设备IO过程中。必须要进行区分。
当然两者似乎也有重合的部分。例如在socket编程(网络编程)网络进程通信过程中,即是一种网络进程通信(两个远程进程),也是一种网络IO过程(一个进程与网卡设备IO)。可以看成两个远程的网络进程通信,也可以看成单个线程与本地网卡设备的IO过程。
所以在某种程度上。可以把两个进程的通信,看成某一个用户进程的IO过程,而另一个进程看成是内核。
在这里进行简单说明:进程同步问题和进程通信问题,都应该作为“并发编程”内容的一部分。设备IO问题作为“网络编程”的一部分进行详解。
5 同步异步在不同语言中表现(实例)
同步阻塞
在此种方式下,用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行。
- java 传统的IO和socket网络编程,都是阻塞通信。接收端在接受到数据之前,一直处于阻塞状态。通过多线程实现并行编程。
- 在linux下,select/poll/epoll也是同步阻塞IO,也被称为事件IO,同时堵塞多个IO。不需要多线程,当某个IO可用时,发送IO事件,使用事件驱动的IO实现并行编程。
- 普通B/S模式(同步):提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事。也是通过开启多个浏览器线程实现并行编程。
同步非阻塞
在此种方式下,用户进程发起一个IO操作以后边可返回做其它事情,但是用户进程需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的CPU资源浪费。其中目前JAVA的NIO就属于同步非阻塞IO。
- 用户进程轮训IO信号,虽然没有阻塞,但是在循环中一直确认IO是否完成。效率很低。
异步非阻塞
在此种模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。目前Java中还没有支持此种IO模型
- node.js是单线程的异步非阻塞(事件循环检测通知的机制);
- windows完全端口是机器上有几个cpu创建几个线程的异步非阻塞机制(多线程池 + 事件触发机制)
- linux epoll也是创建合理的线程池的异步非阻塞机制(线程池+记录活跃socket + 边缘事件触发机制 + mmap文件映射内存减少复制开销)。
- ajax请求(异步): 请求通过事件触发->服务器处理(这是浏览器仍然可以作其他事情)->处理完毕
异步阻塞
此种方式下是指应用发起一个IO操作以后,不等待内核IO操作的完成,等内核完成IO操作以后会通知应用程序,这其实就是同步和异步最关键的区别,同步必须等待或者主动的去询问IO是否完成,那么为什么说是阻塞的呢?因为此时是通过select系统调用来完成的,而select函数本身的实现方式是阻塞的,而采用select函数有个好处就是它可以同时监听多个文件句柄,从而提高系统的并发性!










