半同步/半异步的并发模式
概述
异步的并发模式更高效,而同步的并发模式更简单。高效和简单看起来是矛盾的特性,可以通过半同步/半异步的设计共存。一般来讲,底层是异步的,上层是同步的。比如操作系统的 I/O 是按照异步并发模式设计的,然而应用程序调用的 read/write 是同步的。底层往往更注重效率,应用层则要求简单,以简化应用软件的设计。底层软件的开发量相对较少,但是对性能的影响很大,可以接受更复杂的异步设计;应用软件的开发量比较大,涉及的开发人员比较多,且水平参差不齐,所以应当尽量简单。系统中同步的部分和异步的部分通过队列进行衔接。
图片来源:《面向模式的软件体系结构(卷2):用于并发和网络化对象的模式》
lwIP 的并发模式
lwIP 是应用了半同步/半异步并发模式的典型。lwIP 的底层是事件驱动的,是异步的,其提供的 Raw API 就是基于回调的异步的 API,异步的 API 很高效,很省资源,可以在没有操作系统的条件下运作,但是要用 Raw API 设计应用程序,对程序员是一个挑战,要用异步的思维去做设计。在 Raw API 的基础上,包装了 sequential API,即那些以 netconn_ 为前缀的函数,sequential API 是同步的,需要操作系统的支持,使用 sequential API 开发应用就简单多了,其设计思维是同步的,符合人类大脑的常规思维模式。更上一层,将 sequential API 包装成标准的 BSD Socket 接口, BSD Socket 接口在桌面操作系统中被广泛支持,因此程序员很可能已经非常熟悉,这就为程序员提供了同步的而且是很熟悉的接口,这明显又简化了应用开发人员的工作。当然,sequential API 比 Raw API 使用了多得多的资源,sequential API 要求引入操作系统;BSD Socket 又比sequential API 使用更多的资源,因为又包装了一层。
lwIP 的 Raw API 和 sequential API 之间是通过 mbox 衔接的,mbox 衔接同步部分和异步部分,mbox 将 sequential API 的请求放入队列,并使用操作系统同步原语挂起当前线程,这时传递给 Raw API 的回调函数是一个唤醒被挂起线程的函数,当请求完成后,底层异步调用回调函数,回调函数唤醒调用 sequential API 的线程。
底层按异步设计
底层是按异步设计的,那么上层可以直接使用异步来做设计以达到性能的最优,也可以将异步包装成同步机制以降低上层设计的复杂度。这就比较灵活。
如果底层是按同步设计的,同步不能被包装成异步,没有办法获得更好的性能。这就没什么弹性了。
低功耗要求异步设计
低功耗意味着要尽快将事务处理完毕,然后进入休眠,这也就意味着要高效率,异步是高效的。所以,低功耗场合最好使用异步设计,即使为了降低复杂度采用同步设计,也要使用同步非阻塞模式,不能使用同步阻塞模式。
参考文献
- 《面向模式的软件体系结构(卷2):用于并发和网络化对象的模式》 , 5.3 半同步/半异步
- lwIP Wiki Raw/native API