对于同步、异步、阻塞、非阻塞的几点浅薄理
- 2020 年 1 月 19 日
- 筆記
首先我们先来看看:
一、同步与异步 同步/异步, 它们是消息的通知机制(都是通过状态、通知、回调函数来返回结果) 1. 概念解释 同步 所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。
(按照这个定义,其实绝大多数函数都是同步调用(例如sin isdigit等)。 但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。 最常见的例子就是 SendMessage。 该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回。 当对方处理完毕以后,该函数才把消息处理函数所返回的值返回给调用者。)
异步
恰恰与同步相反。异步的概念和同步相对。 当一个异步过程调用发出后,调用者不会立刻得到结果。 实际处理这个调用的部件是在调用发出后, 通过状态、通知来通知调用者,或通过回调函数处理这个调用
(以 Socket为例, 当一个客户端通过调用 Connect函数发出一个连接请求后,调用者线程不用等待结果,可立刻继续向下运行。 当连接真正建立起来以后,socket底层会发送一个消息通知该对象。)
2.拓展两者在实际程序中的异同:
在实际的程序中, 同步消息通知机制:就好比简单的read/write 操作,它们需要等待这两个操作成功才能返回; 同步, 是由处理消息者自己去等待消息是否被触发; 异步消息通知机制:类似于select/poll 之类的多路复用IO 操作, 当所关注的消息被触发时,由消息触发机制通知触发对消息的处理. 异步, 由触发机制来通知处理消息者; 还是回到上面的例子, 轮到你办理业务, 这个就是你关注的消息, 而办理什么业务, 就是对这个消息的处理, 两者是有区别的. 而在真实的IO 操作时: 所关注的消息就是 该fd是否可读写, 而对消息的处理是 对这个fd 进行读写. 同步/异步仅仅关注的是如何通知消息,它们对如何处理消息并不关心, 好比说,银行的人仅仅通知你轮到你办理业务了, 而办理业务什么业务(存钱还是取钱)他们是不知道的. 二、阻塞与非阻塞 阻塞/非阻塞, 它们是程序在等待消息(无所谓同步或者异步)时的状态.
1. 概念解释 阻塞 阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回。
非阻塞 非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
2. 举例说明 继续上面的那个例子, 不论是排队等待,还是使用号码等待通知, 如果在这个等待的过程中, . 等待者除了等待消息之外不能做其它的事情,那么该机制就是阻塞的, 表现在程序中,也就是该程序一直阻塞在该函数调用处不能继续往下执行. . 相反,有的人喜欢在银行办理这些业务的时候一边打打电话发发短信一边等待,这样的状态就是非阻塞的, 因为他(等待者)没有阻塞在这个消息通知上,而是一边做自己的事情一边等待.
三、易混淆的点 很多人也会把异步和非阻塞混淆, 因为异步操作一般都不会在真正的IO 操作处被阻塞, 比如如果用select 函数,当select 返回可读时再去read 一般都不会被阻塞 就好比当你的号码排到时一般都是在你之前已经没有人了,所以你再去柜台办理业务就不会被阻塞. 可见,同步/异步与阻塞/非阻塞是两组不同的概念,它们可以共存组合, 而很多人之所以把同步和阻塞混淆,我想也是因为没有区分这两个概念, 比如阻塞的read/write 操作中,其实是把消息通知和处理消息结合在了一起, 在这里所关注的消息就是fd 是否可读/写,而处理消息则是对fd 读/写. 当我们将这个fd 设置为非阻塞的时候,read/write 操作就不会在等待消息通知这里阻塞, 如果fd 不可读/写则操作立即返回. 四、同步/异步与阻塞/非阻塞的组合分析 _______阻塞____________________非阻塞_____ 同步 | 同步阻塞 同步非阻塞 异步 | 异步阻塞 异步非阻塞 同步阻塞形式: 效率是最低的, 拿上面的例子来说,就是你专心排队,什么别的事都不做。 实际程序中 就是未对fd 设置O_NONBLOCK 标志位的read/write 操作, 异步阻塞形式: 如果在银行等待办理业务的人采用的是异步的方式去等待消息被触发,也就是领了一张小纸条, 假如在这段时间里他不能离开银行做其它的事情,那么很显然,这个人被阻塞在了这个等待的操作上面; 异步操作是可以被阻塞住的,只不过它不是在处理消息时阻塞,而是在等待消息被触发时被阻塞. 比如select 函数, 假如传入的最后一个timeout 参数为NULL,那么如果所关注的事件没有一个被触发, 程序就会一直阻塞在这个select 调用处. 同步非阻塞形式: 实际上是效率低下的, 想象一下你一边打着电话一边还需要抬头看到底队伍排到你了没有, 如果把打电话和观察排队的位置看成是程序的两个操作的话, 这个程序需要在这两种不同的行为之间来回的切换,效率可想而知是低下的; 很多人会写阻塞的read/write 操作, 但是别忘了可以对fd 设置O_NONBLOCK 标志位,这样就可以将同步操作变成非阻塞的了; 异步非阻塞形式: 效率更高, 因为打电话是你(等待者)的事情,而通知你则是柜台(消息触发机制)的事情, 程序没有在两种不同的操作中来回切换. 比如说,这个人突然发觉自己烟瘾犯了,需要出去抽根烟, 于是他告诉大堂经理说,排到我这个号码的时候麻烦到外面通知我一下(注册一个回调函数), 那么他就没有被阻塞在这个等待的操作上面,自然这个就是异步+非阻塞的方式了. 如果使用异步非阻塞的情况, 比如aio_*组的操作,当发起一个aio_read 操作时,函数会马上返回不会被阻塞,
当所关注的事件被触发时会调用之前注册的回调函数进行处理
以上浅薄的理解,是从我结合另一个博客理解总结过来的,或许理解的不是很完善,以尽心尽力!