写点什么

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:002742

评论

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

git 教程 --git cherry-pick 命令

生之欢愉,时间同行

git 程序员 git cherry-pick

通俗易懂!看了不会忘的网络面试知识点

编程 架构 面试

迎战大厂!“金三银四”和春招通过率达95%的Java面试要点集锦

Java 程序员 架构 面试

工作三年,小胖问我:什么是生产者消费者模式?菜到抠脚!

一个优秀的废人

Java 多线程 阻塞队列 生产者与消费者

Kubelet从入门到放弃系列:GPU加持

DCOS

AI gpu Kubernetes 云原生

2021程序员春招必备:Java面试知识点+答案(7大分类 5000字解析)

比伯

Java 编程 架构 面试 程序人生

windows 搭建ftp服务

xiezhr

vsftpd ftp ftp服务 文件服务 3月日更

无线网络的用户隔离功能

2021抖音面经分享:Java进阶核心知识集/算法刷题宝典(金三银四必备)

比伯

Java 编程 架构 面试 程序人生

科技强国梦的百度式注脚:扎根土壤、拥抱变局、眺望星空

脑极体

区块链产业革命:解决融资租赁之谜

CECBC

区块链

白话Go内存模型&Happen-Before

Gopher指北

Go 语言

科学的互联网思想 指引我国网络强国建设稳步前行

CECBC

网络安全

10.scheduler&lane模型(来看看react是暂停、继续和插队的)

全栈潇晨

源码分析 React React Hooks

Wireshark数据包分析学习笔记Day1

穿过生命散发芬芳

Wireshark 数据包分析 3月日更

【回溯算法】经典题:求目标和的组合方案 ...

宫水三叶的刷题日记

面试 LeetCode 数据结构与算法

Docker的三言两语-基础篇

一个大红包

Docker 28天写作 3月日更

Mysql安装

Sakura

项目汇报会复盘

Geek_XOXO

MySQL数据库的安装与使用

若尘

MySQL 数据库

区块链电子合同--电子合同区块链签约平台

13530558032

一桶食用油的数字化

吴俊宇

数字化转型 鲁花

(28DW-S8-Day13) 在线教育班型和角色

mtfelix

28天写作

三十而已

ES_her0

28天写作 3月日更

11.react concurrent mode(并发模式是什么样的)

全栈潇晨

React React Hooks

9.hooks源码(想知道Function Component是怎样保存状态的嘛)

全栈潇晨

源码分析 React React Hooks

一场关于代码注释的争执,引发的三点思考

架构精进之路

编码 经验分享 七日更 3月日更

12.手写迷你react(短小精悍就是我)

全栈潇晨

源码分析 React React Hooks

程序员成长第十九篇:要不要转管理岗?

石云升

程序员 28天写作 职场经验 管理经验 3月日更

dubbo 源码 v2.7 分析:通信过程及序列化协议

程序员架构进阶

架构 RPC 七日更 dubbo源码 3月日更

Elasticsearch Index Management 索引管理

escray

elastic 七日更 28天写作 死磕Elasticsearch 60天通过Elastic认证考试 3月日更

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