7232ac2df4e7b3c6a6a378b7081c9c7b
同步,异步,阻塞,非阻塞

同步与异步、异步与回调

提几个问题

  • 调用的同步和异步的区别
  • IO 的同步异步的区别
  • 有哪些并发编程模式

调用方式的同步异步

同步:就是程序一个语句一个语句顺序执行,很符合人得思维,是线性执行。主动轮询就是同步行为,调用者需要不断地去查看状态是否更新,在状态更新或结果返回之后,调用者才能跳出循环。

异步:调用是同步方式还是异步方式,就是看对于调用的状态变更这个信息,调用者是主动获取的还是被动获取的。

调用方式的阻塞非阻塞

阻塞和非阻塞的区别是调用后被调用者是否立即返回,调用者是不是在等待。 A 调完 B,就在调用处等待(阻塞),直到 B 方法返回才继续执行剩下的代码,这就是阻塞调用。而非阻塞是 A 方法调用 B 方法,B 方法立即返回,A 可以继续执行下面的代码,不会等在这里,不会被该调用阻塞。当某个方法被阻塞了,该方法所在的线程会被挂起,被操作系统的调度器放到阻塞队列,直到 A 等待的事件发生,才从阻塞态转到就绪态。

上面说的是调用方式的同步异步以及阻塞非阻塞之分,这和 IO 的同步异步阻塞非阻塞的概念是不同的也是大家经常混淆的。其次不要将调用的同步和阻塞绑定:同步不一定会阻塞,有可能调用者线程是主动地不断重复调用,不断地查看结果,每次调用实际上是非阻塞的,对于调用者线程而言,也是非阻塞的,它没有卡在某次调用等待在那里。当然从所做的事情(拿到预期的结果,继续做调用后的事情)的角度,我也可以说这里的逻辑被阻塞了。这里的同步阻塞描述的是逻辑,而非调用者线程,这是两个不同的维度

同步阻塞 IO

Read,write这些操作,其实都是应用程序通过内核的系统调用完成的。为了执行read操作,应用程序会调用相应的一个系统调用(system call),此时系统控制权就交给内核,应用程序阻塞,进行等待系统调用完成。内核执行完该系统调用,会向程序返回响应,并把系统调用的数据结构状态等拷贝给应用程序,应用程序得到响应,不在阻塞,继续进行下面的工作。浅显一点得说,就是在调用 read 系统调用时,应用程序会阻塞并对内核进行上下文切换。然后会触发读操作,当响应返回时,数据就被移动到用户空间的缓冲区中。然后应用程序就会解除阻塞。

同步非阻塞 IO

在linux下,应用程序可以通过设置文件描述符的属性O_NONBLOCK,I/O操作可以立即返回,但是并不保证I/O操作成功。

也就是说,当应用程序设置了O_NONBLOCK之后,执行write操作,调用相应的system call,这个system call会从内核中立即返回。但是在这个返回的时间点,数据可能还没有被真正的写入到指定的地方。也就是说,kernel只是很快的返回了这个 system call(这样,应用程序不会被这个IO操作blocking),但是这个system call具体要执行的事情(写数据)可能并没有完成。而对于应用程序,虽然这个IO操作很快就返回了,但是它并不知道这个IO操作是否真的成功了,如果想 知道,需要应用程序主动地去问kernel。

异步阻塞 IO

应用程序要执行read操作,因此调用一个system call,这个system call被传递给了kernel。但在应用程序这边,它调用system call之后,并不等待kernel返回response,这一点是和前面两种机制不一样的地方。这也是为什么它被称为异步的原因。但是为什么称其为阻塞 呢?这是因为虽然应用程序是一个异步的方式,但是select()函数会将应用程序阻塞住,一直等到这个system call有结果返回了,再通知应用程序。也就是说,“在这种模型中,配置的是非阻塞 I/O,然后使用阻塞 select 系统调用来确定一个 I/O 描述符何时有操作。”

所以,从IO操作的实际效果来看,异步阻塞IO和第一种同步阻塞IO是一样的,应用程序都是一直等到IO操作成功之后(数据已经被写入或者读取),才开始进行下面的工作。异步阻塞IO的好处在于一个select函数可以为多个描述符提供通知,提高了并发性。

异步非阻塞 IO

应用程序提交read请求的system call,然后,kernel开始处理相应的IO操作,而同时,应用程序并不等kernel返回响应,就会开始执行其他的处理操作(应用程序没有被IO操 作所阻塞)。当kernel执行完毕,返回read的响应,就会产生一个信号或执行一个基于线程的回调函数来完成这次 I/O 处理过程。

所谓的同步和异步,在这里指的是application和kernel之间的交互方式。如果application不需要等待 kernel的回应,那么它就是异步的。如果application提交完IO请求后,需要等待“回执”,那么它就是同步的。

而阻塞和非阻塞,指的是application是否等待IO操作的完成。如果application必须等到IO操作实际完成以后再执行下面的操作,那么它是阻塞的。反之,如果不等待IO操作的完成就开始执行其它操作,那么它是非阻塞的。

top Created with Sketch.