搞不清楚同步,阻塞,异步,非阻塞这些概念

2013-04-06 09:51:35 +08:00
 pythonee
同步,阻塞,异步,非阻塞,这几个概念实在是容易把人搞乱,查了很多资料,发现线程中的阻塞/非阻塞和IO中的概念还有点不一样,这又让人更加混乱了。

现在,我只想搞清楚这几个概念在IO中的含义与实例,在理解这些概念的时候,没有实例,实在是有点难弄清楚。另外,java中的nio到底是异步非阻塞还是同步非阻塞啊,我见到了两种说法。我想,弄清楚上面那些概念后,这个答案也自然出来了。

另外,有没有同步非阻塞或异步阻塞模型的实际用例啊
9551 次点击
所在节点    编程
30 条回复
gamexg
2013-04-06 11:25:58 +08:00
阻塞就是指这个操作在完成前被调用函数不会返回,会把当前线程一直阻塞在那里。
非阻塞就是不管进行的操作的完成情况,被调用函数立刻返回,当前线程继续执行下一步。
同步一般可以理解为实际的操作是在当前线程执行的,异步一般指被调用函数内部建立了(也可能使用已存在的)一个新的线程来执行实际的操作,而不是当前线程。
denger
2013-04-07 12:38:26 +08:00
异步/同步:比如 Email 的场景就是异步的,当你发送邮件后,成功与否你无法马上得到响应(比如出现 to address 不存在时),但它此时并没有阻塞(异步非阻塞),所以异步的本质是,当消息发送到对方没有立即得到期待的回应。 反而 HTTP 就是就是同步的,发送请求,并等待对方(server)马上得到回应,当然此时该 thread 处于阻塞的状态(同步阻塞)。

阻塞/非阻塞:阻塞IO和非阻塞IO的区别在于第一步,发起IO请求是否会被阻塞(如线程是否被挂起),如果阻塞直到完成那么就是传统的阻塞IO,如果不阻塞,那么就是非阻塞IO。

再说说以上两者的关系,异步IO的通常都是非阻塞IO的,但是非阻塞IO并不一定就是异步IO,所以不存在 "异步阻塞" 的说法。另外,阻塞IO肯定也是同步IO,反之,则不一定。

关于Java,准确的说,同步阻塞是 BIO;同步非阻塞是 NIO;异步非阻塞是 AIO。可能有些人会说 NIO 是阻塞的,其主要原因我想是因为 Epoll,因为 Java 在 Linux 下是基于它实现,而 Epoll 的IO处理仍然会存在 wait 的过程,所以 Epoll 是阻塞的。(参考: http://stackoverflow.com/questions/7209057 )。 关于 Java NIO 可以参考:http://tutorials.jenkov.com/java-nio/nio-vs-io.html
BOYPT
2013-04-07 12:57:31 +08:00
这些概念都是从现实当中抽象出来的啊,举例子:

同步:必须先吃完饭,再洗碗。

阻塞:你请了个专门负责洗碗的工人,他一天到碗就等着洗碗,你吃饭的时候,碗被你拿着,洗碗工人就被阻塞了,必须等待,等到你吃完饭这个过程完成同步后,才开始洗碗。

异步:那样的工人太懒了,怎么可能只洗碗呢,你吃饭的时候他不能洗碗,但是能擦窗啊,所以虽然洗碗这个工作被阻塞了,但是工人还在做其他不需要等待你吃完饭的工作,这是异步。

非阻塞:因为洗碗这个过程必须要先获得碗,所以洗碗是不能是非阻塞的,但是工人可以随时问:“楼主你吃完饭了嘛”,你必须随时回答,这样的过程是非阻塞的。

可见,阻塞与否是跟“碗”有关的,同/异步是跟工人有关的,在不同的对象角度就有不同的状态表达。
denger
2013-04-07 13:07:08 +08:00
再补充:
POSIX IO模型分布主要有,blocking IO,Non-blocking IO, IO multiplexing, signal driven IO, asynchronous IO. 前四种都是同步IO,只有最后一种为异步IO;

通常同步IO与异步IO的主要区别在于:是否会阻塞请求;那么为什么 Non-Blocking IO 是既然字面意思是 非阻塞IO 而却是同步的呢? 主要在于 Non-Blocking IO 并非完全的不阻塞(IO请求分为两个阶段),而是在数据准备阶段不阻塞,数据拷贝阶段阻塞(参考 Epoll 原理);

注:以上为个人的一些理解!仅供参考。
sillyousu
2013-04-07 13:19:47 +08:00
Unix Network Programming(Unix 网络编程)的第一卷的第六章的第二节。
luoqeng
2013-04-07 14:59:24 +08:00
同步,異步,是目的。阻塞,非阻塞是實現方式。

注:以上为个人的一些理解!仅供参考。
pythonee
2013-04-07 22:56:02 +08:00
@denger
@sillyousu

确实,读了之后清楚了
pythonee
2013-04-07 23:05:04 +08:00
@denger 另外,我建议,还是不要把NIO叫作同步非阻塞IO,直接就叫IO复用是最合理的,同步非阻塞不准确且容易让我们这些初学者误会
pythonee
2013-04-08 08:46:29 +08:00
@denger
@sillyousu
@BOYPT

我还想问问各位,非阻塞IO中,去询问系统数据准备好没好,是不是对开发者透明的?还是需要应用自己去询问,另外,询问过程中,应用有没有让出cpu
BOYPT
2013-04-08 09:58:52 +08:00
@pythonee 非阻塞IO一般是不需要询问的,比如linux中打开了async的fd,read操作不管有没有数据都会马上返回,没数据就返回空值,不存在让出cpu的情况。
pythonee
2013-04-08 10:15:16 +08:00
@BOYPT

不对吧,应用在内核还未准备好数据的时候,是要不断询问的,当然,内核也是果断返回错误的,就像你说的,不断询问“楼主你吃完了吗?” 这样的问题

另外,java中把socket设置non-block就这句话

SocketChannel.configureBlocking(false)

我理解这句话就是通过非阻塞去读取,貌似应用就没有做什么了
heroicYang
2013-04-08 22:18:54 +08:00
@luoqeng 反了吧?阻塞、非阻塞是目的,同步、异步是实现。
sillyousu
2013-04-08 22:21:25 +08:00
>应用在内核还未准备好数据的时候,是要不断询问的

用户进程去可以不断询问一个 non-blocking socket,但是一般不这么做,这样效率很低。

假如只有一个 socket 需要处理,直接 用阻塞 socket 或者 用阻塞 socket + 多线程。好像怎么都很好办。

个人觉得:非阻塞 I/O 一般在处理多个 socket + 很多数据的时候比较好用。
非阻塞 I/O 一般和 I/O 复用 (例如 select) 联合在一起使用。

还是 UNP ,第一卷的十六章,那里讲了非阻塞 I/O 的几种用法。

我搬运一点点,用来说一下 非阻塞 I/O 对 阻塞 I/O 的优势:

blocking socket 用 connect 发起连接,
TCP 需要连接需要等peer发回ack,所以这是会阻塞的。
如果用 non-blocking socket 那么虽然 TCP 连接尚未真正建立起来,但 connect 立即返回,
用户进程需要建立多个连接的时候就会比较有用。

再有是,通过 select 选择出来一个可以写的 socket ,
往这个 socket 做写入操作,如果这是 blocking socket 而且缓冲区剩余空间不够的话就会阻塞。

如果是 non-blocking socket 那么会有多少缓冲空间就写多少,而不会停下来等。
BOYPT
2013-04-09 08:45:35 +08:00
@pythonee 不断询问是一个直接方式,但是实际上效率都太低了;最传统的select模型里面就是,注册了一系列读写fd,然后阻塞在一个accept请求上(可设置超时),只要fd们可读/可写,就返回。传统的模式就是开一个线程专门做这个事情。

SocketChannel.configureBlocking(false)

就是把socket设置成非阻塞模式,实际上究竟是否读写成功,按照具体的返回值判断。
fangzhzh
2013-04-09 09:15:48 +08:00
两篇文章: 各种io模型, 有图有真相. 撸主不要说是csdn的,或者cppblog看不上眼, 看一看,内容很不错

再谈select, iocp, epoll,kqueue及各种I/O复用机制: http://blog.csdn.net/shallwake/article/details/5265287

[翻译] 两种高性能I/O设计模式(Reactor/Proactor)的比较: http://www.cppblog.com/pansunyou/archive/2011/01/26/io_design_patterns.html
pythonee
2013-04-09 10:28:17 +08:00
@sillyousu
@BOYPT

我现在都要考虑是否开一个新帖来讨论了,据两位的回答

SocketChannel.configureBlocking(false)

这句话都是立即返回,那么这里的"非阻塞"就和上面的IO模型中的非阻塞不是同一个概念了,那么为何可以做到 “有多少写多少”,那我觉得这种读写适合所有IO模型啊,为何仅仅在IO复用模型中被广泛使用
pythonee
2013-04-09 10:33:02 +08:00
@fangzhzh 怎么会看不上,上面可是很多大牛的文章呢
pythonee
2013-04-09 10:43:40 +08:00
@BOYPT
@sillyousu

或者说,如果我改成

SocketChannel.configureBlocking(true)

那么,对IO复用模型有什么很大的影响吗?这样IO loop会慢下来?

另外,回到非阻塞"有多少写多少"那个问题,我调用了write(buffer)后,如果内核没有足够的空间,或是buffer = fd.read(),这时应用空间没有足够大的buffer,那么按非阻塞的情况,那么是有多少写多少,有多少读多少,那么剩下的咋弄呢,下次select中了再读再写?
sillyousu
2013-04-09 12:32:42 +08:00
@pythonee
>那我觉得这种读写适合所有IO模型啊,为何仅仅在IO复用模型中被广泛使用

我想大概是因为非阻塞 I/O 一般用来处理大量数据和*很多* socket 的时候比较有效。
而大量 socket 决定了要使用 I/O 复用。

>SocketChannel.configureBlocking(true)
>那么,对IO复用模型有什么很大的影响吗?这样IO loop会慢下来?
这要看情景。UNP 上面的一个 benchmark 显示:使用非阻塞I/O 比使用阻塞I/O 快了一倍左右。

>另外,回到非阻塞"有多少写多少"那个问题,我调用了write(buffer)后,如果内核没有足够的空
>间,或是buffer = fd.read(),这时应用空间没有足够大的buffer,那么按非阻塞的情况,那么
>是有多少写多少,有多少读多少,那么剩下的咋弄呢,下次select中了再读再写?

是的。要自己维护一个缓冲区,剩下的下次在写。
pythonee
2013-04-10 20:10:39 +08:00
@sillyousu

SocketChannel.configureBlocking(false)

这个非阻塞socket可以说是那个UNP中的非阻塞IO模型吗?总感觉你们说的是,但是需要确认一下

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/65058

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX