本文由 简悦 SimpRead 转码, 原文地址 blog.csdn.net

同步与异步

**同步与异步主要是从消息通知机制角度来说的。**

当一个同步调用发出后,调用者要一直等待返回消息(结果)通知后,才能进行后续的执行;
当一个异步过程调用发出后,调用者不能立刻得到返回消息(结果)。实际处理这个调用的部件在完成后,通过状态、通知或者回调来通知调用者

  1. 如果执行部件用状态来通知,那么调用者就需要每隔一定时间检查一次,效率就很低(有些初学 多线程编程的人,总喜欢用一个循环去检查某个变量的值,这其实是一种很严重的错误);
  2. 如果是使用通知的方式,效率则很高,因为执行部件几乎不需要做额外的操作。但是调用者需要监听通知。
  3. 至于回调函数,其实和通知没太多区别。

阻塞与非阻塞

**阻塞和非阻塞 与程序(线程)等待消息通知时的状态有关(无所谓同步或者异步)。**

阻塞调用 是指调用结果返回之前,当前线程会被挂起,一直处于等待消息通知,不能够执行其他业务。函数只有在得到结果之后才会返回。

有人也许会把阻塞调用和同步调用等同起来,实际上它们是不同的。

  1. 对于同步调用来说,很多时候当前线程可能还是激活的,只是从逻辑上当前函数没有返回而已,此时,这个线程可能也会处理其他的消息

    (a) 如果这个线程在等待当前函数返回时,没有执行其他消息处理,而是处于挂起等待状态,那这种情况就叫做同步阻塞; (b) 如果这个线程在等待当前函数返回时,仍在执行其他消息处理,那这种情况就叫做同步非阻塞;

所以同步的实现方式会有两种:同步阻塞、同步非阻塞;
同理,异步也会有两种实现:异步阻塞、异步非阻塞;

  1. 对于阻塞调用来说,则当前线程就会被挂起等待当前函数返回;

非阻塞指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回

虽然表面上看非阻塞的方式可以明显的 提高 CPU 的利用率,但是也带了另外一种后果就是系统的线程切换增加增加的CPU执行时间能不能补偿系统的切换成本需要好好评估

同步/异步 与 阻塞/非阻塞

  1. 同步阻塞形式
    效率是最低的,
    实际程序中:就是未对 fd 设置 O_NONBLOCK 标志位的 read/write 操作;
  2. 异步阻塞形式
    **异步操作是可以被阻塞住的,只不过它不是在处理消息时阻塞,而是在等待消息通知时被阻塞。**
    比如 select 函数,假如传入的最后一个 timeout 参数为 NULL,那么如果所关注的事件没有一个被触发,程序就会一直阻塞在这个select 调用处
  3. 同步非阻塞形式
    实际上是效率低下的
    很多人会写阻塞的 read/write 操作,但是别忘了可以对fd设置O_NONBLOCK 标志位,这样就可以将同步操作变成非阻塞的了
  4. 异步非阻塞形式
    效率更高
    如果使用异步非阻塞的情况,比如 aio_* 组的操作,当发起一个 aio_read 操作时,函数会马上返回不会被阻塞,当所关注的事件被触发时会调用之前注册的回调函数进行处理

很多人会把同步和阻塞混淆,我想是因为很多时候同步操作会以阻塞的形式表现出来,比如很多人会写阻塞的 read/write 操作,但是别忘了可以对 fd 设置 O_NONBLOCK 标志位,这样就可以将同步操作变成非阻塞的了。但最根本是因为没有区分这两个概念,比如阻塞的 read/write 操作中,其实是把消息通知机制和等待消息通知的状态结合在了一起,在这里所关注的消息就是fd是否可读/写,而等待消息通知的状态则是对fd可读/写等待过程中程序(线程)的状态。当我们将这个 fd 设置为非阻塞的时候,read/write 操作就不会在等待消息通知这里阻塞,如果 fd 不可读 / 写则操作立即返回。

同样的,很多人也会把异步和非阻塞混淆,因为异步操作一般都不会在真正的IO操作处被阻塞,比如如果用 select 函数,当select返回可读时再去read一般都不会被阻塞,而是在select函数调用处阻塞

4 小明的故事

对上面所讲的概念再次进行一个场景梳理,上面已经明确说明,**同步/异步关注的是消息通知的机制,而阻塞/非阻塞关注的是程序(线程)等待消息通知时的状态**。以小明下载文件打个比方,从这两个关注点来再次说明这两组概念,希望能够更好的促进大家的理解。

  1. 同步阻塞:小明一直盯着下载进度条,到 100% 的时候就完成。

    同步体现在:等待下载完成通知;

    阻塞体现在:等待下载完成通知过程中,不能做其他任务处理;

  2. 同步非阻塞:小明提交下载任务后就去干别的,每过一段时间就去瞄一眼进度条,看到 100% 就完成。

    同步体现在:等待下载完成通知;

    非阻塞体现在:等待下载完成通知过程中,去干别的任务了,只是时不时会瞄一眼进度条;【小明必须要在两个任务间切换,关注下载进度】

  3. 异步阻塞:小明换了个有下载完成通知功能的软件,下载完成就 “叮” 一声。不过小明仍然一直等待 “叮” 的声音(看起来很傻,不是吗)。

    异步体现在:下载完成 “叮” 一声通知;

    阻塞体现在:等待下载完成 “叮” 一声通知过程中,不能做其他任务处理;

  4. 异步非阻塞:仍然是那个会 “叮” 一声的下载软件,小明提交下载任务后就去干别的,听到 “叮” 的一声就知道完成了。

    异步体现在:下载完成 “叮” 一声通知;

    非阻塞体现在:等待下载完成 “叮” 一声通知过程中,去干别的任务了,只需要接收 “叮” 声通知即可;【软件处理下载任务,小明处理其他任务,不需关注进度,只需接收软件 “叮” 声通知,即可】

所以,综上所述,同步和异步仅仅是关注的是 消息如何通知的机制,而阻塞与非阻塞关注的是等待消息通知时的状态。也就是说,同步的情况下,是由处理消息者自己去等待消息是否被触发,而异步的情况下是由触发机制来通知处理消息者,所以在异步机制中,处理消息者和触发机制之间就需要一个连接的桥梁

在小明的例子中,这个桥梁就是软件 “叮” 的声音。