关于同步和异步的理解

最近看到 CPyUG 上关于同步和异步的讨论,也在网上看了一些文章,记录一下自己的想法。

首先,相对于内核来说,内核提供两种类型的读取操作:同步和异步,其中同步的读操作又分为阻塞和非阻塞的。

同步阻塞的读取操作是我们最经常用的,我们调用读取方法,内核进行实际的读取,此时的应用程序是被阻塞在读取方法上的,读取完成后,方法返回,我们获得数据,进行数据处理。

应用程序                内核
    |                    |
    |       read()       |
    |------------------->|-
                         |
                         | 等待数据就绪
                         |
                         |-
                         |
                         | 等待数据读取
                         |
    |<-------------------|-读取完成
    |                    |
处理数据

同步非阻塞的读取其实就是当没有数据的时候内核返回特定的错误码,而不是等待数据。

应用程序                内核
    |                    |
    |       read()       |
    |------------------->|-
    |<-------------------|-没有数据立刻返回特定的错误码
    |                    |
    |       read()       |
    |------------------->|-
                         |
                         | 等待数据读取
                         |
    |<-------------------|-读取完成
    |                    |
处理数据

这种形式的应用通常会变成 I/O复用 的形式:

应用程序            中间层(select,epoll)      内核
    |                                         |
    |     register()                          |
    |------------------->|     register()     | 
    |                    |------------------->|-注册读就绪事件
    |                                         |
    |                                         |
    |                           notify()      |
    |      notify()      |<-------------------|-有数据可读
    |<-------------------|                    |
    |        read()                           |
    |---------------------------------------->|-
                                              |
                                              | 等待数据读取
                                              |
    |<----------------------------------------|-读取完成
    |                                         |
处理数据

异步的读取方式,就是我们向内核注册一个读取完成事件,并提供一个 buffer ,当内核将数据读取到 buffer 里之后,再通知我们,我们再处理数据。

应用程序                内核
    |                    |
    |  register(buffer)  |
    |------------------->|-注册读完成事件
    |                    |
    |                    |
    |                    |
    |                    |
    |                    |
    |                    |
    |      notify()      |
    |<-------------------|-读取完成
    |                    |
处理 buffer 中的数据

它通常也会有个中间层来处理一些细节问题:

应用程序               中间层                 内核
    |                                         |
    |  register(buffer)                       |
    |------------------->|  register(buffer)  | 
    |                    |------------------->|-注册读完成事件
    |                                         |
    |                                         |
    |                                         |
    |                                         |
    |                                         |
    |                                         |
    |                           notify()      |
    |      notify()      |--------------------|-读取完成
    |<-------------------|                    |
    |                                         |
处理 buffer 中的数据

同步阻塞的方式是最简单的,不过在单行时,它的处理能力非常有限,通常会使用并行(多进程,单进程多线程,多进程多线程)的方法。不需要高并发的应用大多采用这种方式。需要高并发的应用大多采用 I/O复用 的方式。异步I/O 目前正在发展和完善的过程中,只有期待未来了。

blog comments powered by Disqus