本文内容大致翻译自 libevent-book, 但不是照本翻译. 成文时, libevent最新的稳定版为 2.1.8 stable. 即本文如无特殊说明, 所有描述均以 2.1.8 stable 版本为准.
本文为系列文章的第一篇, 对应libevent-book的 chapter 0 + chapter 1 + R0 + R1
0. 前提条件这个文档是对libevent的介绍与指导, 阅读文档需要你具有以下的能力:
你精通C语言
你至少了解Unix网络编程.
你会安装libevent
你大致知道libevent是干什么用的.
1. 基本概念: 阻塞/非阻塞/同步/异步/回调机制的讨论这里首先要解释四个名词: 阻塞, 非阻塞, 同步, 异步. 它们都是修饰"接口"的形容词, 或者说的土一点, 它们都是修饰"函数"的形容词.
同步, 还是异步, 是从"消息通信"的视角去描述这个接口的行为. 而所谓的消息通信, 你可以简单的把"函数"想象成一个淘宝客服, 把"调用方"想象成你自己. 调用函数的过程其实就是三步:
"你"询问"淘宝客服"一个问题. 比如, "在吗?". 在这个场景中, 你就是"调用方", "淘宝客服"是函数, 而那句"在吗?", 则是函数参数, 你把函数参数传递给函数.
"淘宝客服"进行后台处理. 这时淘宝客服接收到了你的询问消息, 如果他没有在忙, 那么他可以立即回复你. 如果他现在正在忙, 比如正在吃饭, 比如正在和老婆吵架, 比如淘宝客服需要先看一下你之前的行为记录, 然后再决定如何回复你.(比如他看到你正在浏览一双袜子,觉得你在潜在的买家, 他决定回复你. 比如他看到你三天前下单买了一双袜子, 但袜子还没有发货, 他觉得你有退货的风险, 从而决定不理你, 假装不在.) 这个客服思考决断的过程, 就是函数内部进行处理运算的过程. 当然这个例子很简单, 有些牵强.
最终, 淘宝客服回复了你, "在的, 亲". 这里, 回复这个动作, 就是函数返回, 而"在的, 亲"这句话, 就是函数的返回值.
你从这个角度去看, 函数调用, 就是消息通信的过程, 你发送消息给函数, 函数经过一番运算思考, 把结果再回发给你.
所谓的同步, 异步, 指的是:
这个淘宝客服很老实, 对于每个顾客发来的问题, 他都需要经过一番思考, 再进行答复. 这个函数很老实, 对于每个函数调用, 都很老实的根据传入参数进行计算, 再返回结果. 也是是说, 在淘宝客服思考结束之前, 这个客服不会向你发送答复, 你也收不到答复. 也就是说, 在函数运算结束之前, 函数不会返回, 你也得不到返回值. 那么, 这个客服是同步的, 这个函数调用的过程是同步调用, 这个函数是同步的.
假如这个淘宝客服很不老实, 他装了一个自动答复小程序. 对于每个询问的顾客, 都先自动回复一句"亲, 现在很忙哟, 客服MM可能过一会才能给你答复". 也就是说, 顾客在发出询问之后, 立即就能得到一个答复. 也就是说, 调用方在调用一个函数之后, 这个函数就立即返回了. 而真正的结果, 可能在过五分钟之后才会给你. 即是五分钟之后客服对你说"在的呢, 亲". 这样的函数, 就叫异步函数.
异步客服需要解决一个问题: 当真正的运算结果得出之后, 被调用的客服如何通知作为调用方的你, 取走答案. 在淘宝客户端上, 是通过手机的震动消息提醒, 是通过聊天框的红点.
所以, 关于同步, 和异步, 这里做一个稍微正式一点的总结:
同步的过程: 调用方传参->函数运算->函数返回运算结果.
异步的过程: 调用方传参->函数说我知道了, 然后过了五分钟, 函数说我算出来了, 结果在这里, 你来取.
这里我们着眼于消息的传递, 通讯方式, 也就是站在函数的角度去看, 结果是如何传递给调用方的. 同步接口, 运算结果在"函数调用"这个场景下就返回给了调用方. 异步接口: 运算结果在"函数调用"这个场景之后的某个不定的时刻, 通过某种通知方式, 传递给调用方.
整个过程中我们忽略了一件事: 就是, 在函数执行运算的过程中, 调用方在干什么. 也是是, 在淘宝客服内心思考如何回复你的时候, 你在干什么.
这就引出了阻塞与非阻塞:
阻塞: 在函数执行运算的过程中, 当前线程什么也做不了. 在等待客服回复的过程中, 你什么也不做, 就在那干等着, 直到他回复了你.
非阻塞: 在函数执行去处的过程中, 当前线程可以去做其它事情. 在等待客服回复的过程中, 你上了个厕所, 还顺便洗了个澡.
换句话说:
同步与异步, 描述的是 被调用的函数, 如何将结果返回给调用者
阻塞与非阻塞, 描述的是 调用方, 在得到结果之前能不能脱身
这是两个维度上的逻辑概念, 这两个维度互相有一定的干涉, 并不是完全正交的两个维度, 这样, 既然是两个维度, 那么就有四种组合.
同步, 且阻塞: 调用方发起调用直至得到结果之前, 都不能干其它事情. 被调函数接收到参数直到运算结束之前, 都不会返回.