堵塞和非堵塞 | 同步与异步
故事:老王烧开水。
出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。
有好几种等待方式:
1.老王用水壶煮水,并且站在那里,不管水开没开,盯着水壶看水开了没。-同步阻塞
2.老王还是用水壶煮水,不再傻傻的站在那里看水开,而是开始一边玩手机一边等水开,每隔一段时间看看水开了没有,水没有开就继续刷手机。-同步非阻塞
3.老王还是使用响水壶煮水,跑到客厅上网去,等着响水壶自己把水煮熟了以后通知他。-异步非阻塞
以上的烧开水故事,一共有三个对象,分别是:
- 老王
- 水壶(响水壶、水壶)
- 开水
类比于IO过程中的三个角色:
- 应用程序(老王)
- 内核(水壶)
- IO的数据(开水)
对于老王而言,他要的就是开水,对于应用程序而言,最终要的就是数据。
老王想要拿到开水,向水壶发出请求;对于程序而言,就是应用程序向内核发起一个读数据的请求。
一、同步堵塞
老王在用水壶烧开水时,就一直盯着水壶看它开了没有,此时,他啥都不能干,那都不能去。
类比于应用程序IO,就相当于应用程序发起一个IO请求,以读取数据为例,此时需要进行一次系统调用,内核由用户态切换到内核态,内核开始跟硬件设备进行交互并从硬件设备中读取数据,此时可能硬件设备还没有接收、返回数据,所以内核函数一直堵塞,直到数据到达才进行返回。这就是同步堵塞。
优点:能够及时返回数据,无延迟;方便调试;
缺点:需要付出等待的代价;
二、同步非堵塞
老王在用水壶烧开水时,可以一边刷手机一边等水壶开,它并不需要一直等待着什么都不干。
类比于应用程序IO,当应用程序发起了一次读取数据的请求,还是会发起系统调用内核,但是这次内核根据硬件中是否有数据来执行不同的操作,如果有数据,那么将数据拷贝到用户空间,如果没有数据也可以返回一个标志,比如-1。应用程序不间断的去查询结果(轮询),而不需要一直等待结果,这期间也可以执行其他事情。
优点:无需等待结果,能够同时执行别的操作;
缺点:应用进程持续轮询内核,以查看某个操作是否就绪,这么做往往消耗大量的CPU时间,这会导致整体数据吞吐量的降低。;
三、异步非堵塞
(有些文章里说还有‘异步堵塞’,但是异步堵塞是没有意义的,都已经异步了,何须堵塞?)
虽然在烧开水,但老王却在安心上网,因为他知道响水壶水开了之后会有声音提醒他,也就不用一直盯着或一直间断的去查看了。
类比应用程序IO,应用程序只需要发起一次读取数据的请求,接下来就等着内核将数据拷贝到用户空间,并且内核将数据拷贝完成后会通知应用程序,在整个过程中程序可以继续往下执行。
优点:无需等待返回,提升吞吐量;
缺点:降低了相应时间。
四、总结
堵塞/非堵塞:描述的是调用者调用方法后的状态,例如:线程A线程调用B方法,A线程处于堵塞状态。
同步/异步:描述的是方法跟调用者通信的方式,如果不需要调用者主动等待,即调用者调用方法后立刻返回,通过方法本身的回调、消息通知等方式通知调用者就是异步(例如:响水壶发出声音通知老王水开了)。如果调用方法后一直需要调用者等待方法返回结果,那么就是同步的(例如:老王等待着水壶开)。