打从敲下"Hello World!",咱们算入行了。我开始写代码,和用计算器差不多:
代码一路顺着走,不开叉。
生涯第1个波折,是学到条件判断,像这样:
代码开始有不同走向,有点绕,还能驾驭。
再后来就遇到人生大哉问:妈和媳妇一起掉河里,你救谁?
翻译一下,如何同时运行代码A和代码B(并行)?
为一起救,我学过各种泳姿:
焦头烂额,终于hold住。
可人生问题哪止这些,后来更大问题是3字:“没钱”
“多线程”虽好,太费钱(资源消耗大)、太折腾(竞争成本高),很多场景受限:
吐血。
小品文已过,咱们言归正传。
近些年,很多编程语言都修订标准,新增2个关键字async/await:
所有目的,都是为更好的支持异步编程,不约而同的加强 Coroutine。
Coroutine(协程) 是老东西(比 UNIX 都早),近些年的广泛使用有其背景:
嗯,这段像瞎掰,实际是典型 JavaScript 开发者心态,其它也类似。
远的不说,做嵌入式开发,也常被异步 IO 编程(基于中断 / 消息 / 事件)搞得焦头烂额,我也想要“Code like sync, works like async”。
可 C/C++ 如何做 Coroutine 呢?
方案 1:
基于用户态 Scheduler(微信后台方案)
Coroutine 是简单的东西,第一反应就是拿用户态 Scheduler 实现。
即任务切换时,完成真实的换栈:
任务运行上下文 (context) 可以简视:寄存器 / 栈信息,当每个协程任务放弃 (yield) 时,执行 context 切换。看起来和 OS 线程没区别,实际是有的:
对大量链接的服务器后台,上述优势明显。可以参见腾讯开源协程库: libco。
这是对调用者干净的实现,编程没有限制,PC 端主流协程库都如此实现:
换栈的手段也不局限汇编,有系统库或第三方:
尽管如此,可对嵌入式编程意义很小:
方案 2:
基于标签语法糖 (switch 或 goto 扩展)
这是借用预编译宏和标签,大玩语法游戏的套路。
目前所有类似实现 (如 Protothread),都基于 Simon Tatham 的文章,这里简要 1 种核心:
仔细看上述代码,它是合法的。在 switch/case 间,插入不同的语句,用不同 case 分割。由此换个视角看,count 可视为状态索引,每次进入时会恢复到代码对应位置。这是一种不基于换栈的任务切出 / 切回方式。
把上述 switch/case 手段用宏 crBegin/crFinish/crReturn 包装下,下面两段带 while(1) 的死循环代码,就可以在 1 个线程中并发执行:
这是 Coroutine 实现的最轻方式,优点:
不过,不知道大家发没发现,局部变量该怎么办?
没有换栈,无法用局部变量,必须 static 静态化。由此,这种方式受限明显:
看的出来,让调用者有些难受。
由此,Contiki OS 又对上述方法改良并包装,并用这种机制实现整个操作系统的多任务。
它的代码值得一读,除了这部分,其它模块 (uIP/GUI/Timer/Mem) 都很漂亮。
大家看的出来,嵌入式系统上 1 和 2 还不够完美,有没有更好的方案?希望:
显然需要新方案。
这里卖个关子,感兴趣的朋友可以留言讨论,请持续关注我们公众号哦~
作者介绍:
王相宇,滴滴两轮车硬件技术部
本文转载自公众号普惠出行产品技术(ID:gh_ed6841067977)。
原文链接:
领取专属 10元无门槛券
私享最新 技术干货