写点什么

Code Like Sync, Works Like Async

  • 2020-10-20
  • 本文字数:2042 字

    阅读完需:约 7 分钟

Code Like Sync, Works Like Async

前言.

打从敲下"Hello World!",咱们算入行了。我开始写代码,和用计算器差不多:


  • 输入:1 + 1

  • 结果:2


代码一路顺着走,不开叉。


生涯第 1 个波折,是学到条件判断,像这样:


  • a = 1,b = 1

  • if a + b = 2 print "这都知道?"else print “答对了”


代码开始有不同走向,有点绕,还能驾驭。


再后来就遇到人生大哉问:妈和媳妇一起掉河里,你救谁?


翻译一下,如何同时运行代码 A 和代码 B(并行)?


为一起救,我学过各种泳姿:


  • 开始用 “定时器/软中断”挺好,不过随着媳妇数(代码块)的增多,猝!

  • 后来折腾“事件驱动/状态机”,就活在连跳/回调地狱里,最终为个 delay 而累猝!

  • 最后“多线程”还行…还活着。


焦头烂额,终于 hold 住。


可人生问题哪止这些,后来更大问题是 3 字:“没钱”


“多线程”虽好,太费钱(资源消耗大)、太折腾(竞争成本高),很多场景受限:


  • 浏览器:没有多线程

  • 嵌入式 MCU:实时性要求高、RAM 受限,甚至没有 RTOS

  • 大链接数 server:微信后台

  • 游戏:魔兽世界


吐血。


小品文已过,咱们言归正传。

1.

近些年,很多编程语言都修订标准,新增 2 个关键字 async/await:


  • 直接包含:C#/JavaScript/Rust/Dart

  • 其它语言(golang/erlang/lua…)没直接包含,也提供类似机制 ,比如 Go goroutine


所有目的,都是为更好的支持异步编程,不约而同的加强 Coroutine。


Coroutine(协程)是老东西(比 UNIX 都早),近些年的广泛使用有其背景:


  • 大量线程用不起(服务器或嵌入式),或者没有线程(浏览器或 MCU 裸机)

  • 然而,异步需求在,开源库 API 设计成异步 IO 渐变主流

  • 可异步代码太难搞,程序一膨胀,不是回调地狱就是全程乱跳。。。

  • 由此不约而同想:代码能不能写起来像同步,跑起来像异步?

  • 做梦之余一看,这不是就是 Coroutine 吗?好好,不止第三方库,要语言层支持,加 promise,再加 async/await


嗯,这段像瞎掰,实际是典型 JavaScript 开发者心态,其它也类似。


远的不说,做嵌入式开发,也常被异步 IO 编程(基于中断/消息/事件)搞得焦头烂额,我也想要“Code like sync, works like async”。


可 C/C++如何做 Coroutine 呢?

2.

方案 1


基于用户态 Scheduler(微信后台方案)


Coroutine 是简单的东西,第一反应就是拿用户态 Scheduler 实现。


即任务切换时,完成真实的换栈:



任务运行上下文(context)可以简视:寄存器/栈信息,当每个协程任务放弃(yield)时,执行 context 切换。看起来和 OS 线程没区别,实际是有的:


  • 所有 Coroutines 共享 1 个 CPU 线程,相互无抢占(协作式,竞争成本低)

  • Coroutine 更轻,尽管 Linux 中单线程只需约 4KB(1 页),然而 1 个 Coroutine 只 0.1KB

  • Coroutine 切换快(约 100T),在多数平台上消耗时间在纳秒级/微秒级


对大量链接的服务器后台,上述优势明显。可以参见腾讯开源协程库: libco。


这是对调用者干净的实现,编程没有限制,PC 端主流协程库都如此实现:


  • libco(微信)/Boost.Context(C++准标准)/libaco/coroutine


换栈的手段也不局限汇编,有系统库或第三方:


  • posix 中 context/Windows 中 fiber/C 标准中 setjump 等。


尽管如此,可对嵌入式编程意义很小:


  • RTOS 中 Task 实现类似,内存消耗/切换成本已经很低

  • RTOS 中 Task 亦可配置成协作式(非抢占)

  • 每种 CPU 都需要移植,等价于再实现 Scheduler,意义不大


方案 2


基于标签语法糖(switch 或 goto 扩展)


这是借用预编译宏和标签,大玩语法游戏的套路。


目前所有类似实现(如 Protothread),都基于 Simon Tatham 的文章,这里简要 1 种核心:



仔细看上述代码,它是合法的。在 switch/case 间,插入不同的语句,用不同 case 分割。由此换个视角看,count 可视为状态索引,每次进入时会恢复到代码对应位置。这是一种不基于换栈的任务切出/切回方式。


把上述 switch/case 手段用宏 crBegin/crFinish/crReturn 包装下,下面两段带 while(1)的死循环代码,就可以在 1 个线程中并发执行:



这是 Coroutine 实现的最轻方式,优点:


  • 完全标准 C 实现,跨平台

  • 每个 Coroutine 只需要额外 1 字节 - 2 字节内存

  • Task 切换无成本


不过,不知道大家发没发现,局部变量该怎么办?


没有换栈,无法用局部变量,必须 static 静态化。由此,这种方式受限明显:


  • 如果用 static 变量,会导致函数无法重入

  • 如果不用 static,就必须传参类似 ctx 参数来代替局部变量


看的出来,让调用者有些难受。


由此,Contiki OS 又对上述方法改良并包装,并用这种机制实现整个操作系统的多任务。


它的代码值得一读,除了这部分,其它模块(uIP/GUI/Timer/Mem)都很漂亮。

3.

大家看的出来,嵌入式系统上 1 和 2 还不够完美,有没有更好的方案?希望:


  • 只用 C99 实现,完全跨平台/跨架构

  • 内存消耗要远小于线程,额外消耗在几个字节内

  • 可以使用局部变量(至少表面上)

  • 对调用者友好,类似其它语言 async/await 的方式


显然需要新方案。


这里卖个关子,感兴趣的朋友可以留言讨论,请持续关注我们公众号哦~


作者介绍


王相宇,滴滴两轮车硬件技术部


本文转载自公众号普惠出行产品技术(ID:gh_ed6841067977)。


原文链接


Code Like Sync, Works Like Async


2020-10-20 14:003124

评论

发布
暂无评论
发现更多内容

我们该如何应对日益增长的安全债务风险?

鉴释

应用安全 代码安全

围观|解读新一代企业数字化架构的“三驾马车”

尔达Erda

开源 DevOps 云原生 数字化转型 数字化

成为Linux大佬的学习之路-规划

学神来啦

Linux 运维 linux运维 linux学习

CloudQuery 如何实现云上数据导入导出

BinTools图尔兹

数据库 dba 国产数据库 运维开发

架构实战训练营模块三课后作业

Clarke

鉴释人物 | 专访产品开发总监吴翔:DevSecOps的竞技之道

鉴释

敏捷开发 应用安全 软件安全 软件质量与安全

揭秘百度微服务监控:百度游戏服务监控的演进

百度开发者中心

云计算 微服务 最佳实践 方法论 云原生

2021全国人工智能师资培训走进北理工,百度飞桨助力高校教师提升AI能力

百度大脑

人工智能 高校

如何使用 Kind 快速创建 K8s 集群?

尔达Erda

开源 云原生 k8s PaaS kind

【报名】百度EasyDL研讨会:揭秘智能化硬件AI应用的技术难点与行业落地

百度大脑

人工智能 智能化

鉴释课堂丨编译器技术入门知识一网打尽

鉴释

编译器 编译器原理

性能测试误差对比研究(二)

FunTester

性能测试 压测 接口测试 测试框架 误差分析

第八周作业——知识星球内容管理功能

小夏

产品经理训练营 邱岳

51CTO专访|鉴释首席架构师刘新铭:让软件开发更安全、更高效

鉴释

编译器

创业邦专访丨兼容国内外市场的代码分析软件,鉴释科技帮助企业减少bug发生率

鉴释

创业公司

六种主要服务器管理协议简单概述-行云管家

行云管家

行云管家 服务器协议 服务器管理

鉴释×RT-Thread丨2020 RT-Thread开发者大会精彩回顾

鉴释

操作系统 物联网

fil价格今日行情?2021年fil币预估价格多少钱一枚?

区块链 分布式存储 IPFS fil价格 fil预测

鉴释人物丨专访首席技术官陈新中:三十年磨一“鉴”, 打造静态代码分析行业标杆

鉴释

编译器 静态代码分析

鉴释人物丨专访首席科学家李隆: 重一步业务逻辑验证,省百步漏洞补缺

鉴释

业务逻辑 漏洞修复

即时通信 IM 产品怎么选? 本文超详细解说,马住!

腾讯云音视频

图灵奖得主Judea Pearl谈机器学习:不能只靠数据

百度开发者中心

机器学习 最佳实践 方法论 文化 & 方法 其他

论工作几年深陷业务代码的程序员如何实现自我提升

北游学Java

Java 程序人生

直播回顾丨鉴释首席架构师刘新铭为您解读“第一性原则”

鉴释

软件开发 代码质量 软件质量与安全

百度前端三面面试题全部公开,三面的最后一个问题令我窒息

前端依依

百度 面试 大前端 经验总结

工程师文化落地的几点思考

baiyutang

敏捷 敏捷开发 精益开发 精益思想 敏捷管理

性能测试误差对比研究(一)

FunTester

性能测试 接口测试 测试框架 误差分析

认识容器,我们从它的历史开始聊起

华为云开发者联盟

Docker Kubernetes 容器 进程

鉴释×中科院计算所丨OS2ATC 2020精彩回顾

鉴释

开源 操作系统

RTC月度小报5月丨教育aPaaS灵动课堂升级、抢先体验VUE版 Agora Web SDK、声网Agora与HTC达成合作

RTE开发者社区

月度小报 行业深度

我们三岁啦!

鉴释

Code Like Sync, Works Like Async_编程语言_王相宇_InfoQ精选文章