为了阐释方便,需要先引入一个概念:
比进程小的可以是线程,比线程还小的是什么呢?--协程;
协程:在线程中一个不带返回值的函数调用叫做协程。(我们暂且这样定义)
比如一个线程执行了以下流程:
做饭-吃饭
那么做饭的过程就叫协程,吃饭的过程也叫协程。
→同步、异步在阐述场景的时候总是容易和阻塞与非阻塞混淆,其实他们是不同维度的概念。
(1)同步、异步: 描述的是协程之间的关系;
(2)阻塞、非阻塞:描述的是一个协程的属性;
这里的"协程"不能换成"线程",因为单"线程"也可以通过某种办法维护很多并行的协程,比如IO复用。
这里的"协程"也不能换成"事件",因为"事件"不是用于描述过程的;
这里的"协程"也不能换成"函数",因为"函数"是代码中的概念;
这里的"协程"也不能换成"任务",因为"任务"有可能是一个流水线,甚至可能要多个线程完成。
1、阻塞:协程执行过程中会让线程挂起;
比如:A在焖米饭,没看焖好就一直等在电饭锅边,什么也不干;
2、非阻塞:协程在执行过程中没有完成也返回;
比如:A在焖米饭,看见没焖好米饭,也去干别的事了;
但是A需要隔一会就看一下米饭是否熟了。
所以在非阻塞的实现过程中,为了保证得到结果,有时需要设计轮询的方式来实现。
比如:《Unix环境网络编程》中,第六章说的recvfrom
3、同步:一件事完成之后才能做另一件事,与线程和进程数基本无关,因为它描述的是协程之间的关系。
比如:做好饭后才能吃饭。也可以细分成多种情况:
A做好饭后,A才能吃饭;(一般的函数都是这个范畴:如read/write)
A做好饭后,B才能吃饭;(锁、互斥量就是解决这个问题的)
A做好饭后,ABCDEF...才能吃饭;(条件变量就是解决这个问题的)
4、异步:与同步相反,做事并不依赖于其他事产生的结果。
比如:A做好饭了我就吃,没做好饭我也不会张着嘴在饭桌前干等着,该干什么干什么。
Linux中通常说的AIO,epoll(),libevent等都是异步的主要应用。异步通常和“事件”联系在一起,一般是先把描述符绑定到事件上,当事件发生后,触发注册的回调函数。
异步实现有两种方式:
(1)信号:A做好饭之后,敲个钟;
(2)回调函数:饭做好了之后,挨个叫吃饭。
综上,可以得到以下结论:
阻塞是实现同步的一种手段。(锁、互斥量、条件变量等的实现都是要让线程挂起)
轮询时保证非阻塞函数能够得到肯定结果的一种手段。(另外一种方式就是需要用异步的方式触发了)
异步可以是保证非阻塞函数得到肯定结果的一种手段。
如果看着有些糊涂,那么我们用Linux的IO来介绍一下阻塞、非阻塞、同步、异步这些概念:
基本的Linux IO矩阵
(1)同步阻塞:
如read/write,在结果返回之前,当前线程会被挂起,函数只有在得到结果之后才会返回。recvfrom举例如下:
(2)同步非阻塞:
如read/write的fd指定O_NONBLOCK,在不能立刻得到结果之前,函数不会阻塞当前线程,而会立刻返回。
当前线程也可以执行其他的操作,但得到的返回值是不一样的,如果返回值比较重要,就需要轮询,比如recvfrom函数
虽然不会阻塞进程,但需要不断轮询来查询结果:
(3)异步阻塞:
如IO复用中的select, 同时维护着多个文件描述符,只要有一个描述符返回时,返回值就大于0。
当然,select函数实现了单线程的异步,其实如果每个线程都维护一个任务,任务之前通过锁来保证执行顺序,这才是通常意义上的异步阻塞。recvfrom举例如下:
(4)异步非阻塞:
如AIO,linux中的epoll,先把函数注册给相应的事件,当满足条件之后,触发相应的操作。recvfrom举例如下:
如有谬误,请多指教,本文参考以下大神博客:
http://blog.csdn.net/heyutao007/article/details/7065166
http://blog.chinaunix.net/uid-26000296-id-3754543.html
http://blog.chinaunix.net/uid-26000296-id-3754118.html