当前位置: 首页 > news >正文

2603C++,简单实现协程

如何实现简单协程?

什么是异步高并发,说白了,它就是个可自己控制挂起和继续的函数.
接着100多行代码,让你彻底看清楚,协程到底是怎么回事?

一,为何协程?

写过网络编程的,谁又没被回调地狱折磨过呢?
简单业务,用异步来写,性能是上去了,代码就变成了一坨.

voidon_request(Request req){db_query(req.id,[](DBResult res){if(res.ok){rpc_call(res.data,[](RPCResult rpc_res){if(rpc_res.ok){/*...*/}else{/*...*/}});}else{/*...*/}});}

逻辑被撕得粉碎,可读性烂到家.
想要的是既有异步的性能,又有同步代码的清爽.

协程,就是为了让你可用同步的写法,干出异步的活儿.

它允许你在耗时操作上挂起,把CPU让给别人,等完成操作了,再从挂起的地方恢复,继续往下走.

二,从玩具到工具,手撸一个分发器

要实现一个可挂起和恢复的函数,只需要三样东西:
1,保存状态,知道执行到哪了,
2,恢复入口,显式从哪继续,
3,及一个分发器,也就是谁来调用.

思路就是,用一个状态变量当PC,用switchcase跳转表.这套组合拳就是无栈协程的精髓.

现在,直接从该思路出发,搭一个可模拟网络分发事件的架子.

需要一个Scheduler分发器,它知道哪些协程就绪队列里,哪些在等待队列中.
协程需要IO时,就从就绪队列移动到等待队列里去,然后让出CPU.

IO事件来了,分发器再把它挪回就绪队列.看代码:

#include<cstdio>#include<queue>#include<map>//`协程`基类和核心宏structCoroutine{intstate=0;boolfinished=false;virtual~Coroutine()=default;voidresume(){if(!finished)step();}protected:virtualvoidstep()=0;};#defineCORO_BEGIN()switch(state){case0:#defineCORO_YIELD()do{state=__LINE__;return;case__LINE__:;}while(0)#defineCORO_END()}finished=true;//分发器classScheduler{std::queue<Coroutine*>ready_queue;std::map<int,std::queue<Coroutine*>>wait_map;public:voidspawn(Coroutine*c){ready_queue.push(c);}//`协程`调用此函数以等待事件voidwait_for(Coroutine*c,intevent){wait_map[event].push(c);}//外部`事件源`调用此函数以通知事件voidnotify(intevent){if(wait_map.count(event)){auto&q=wait_map[event];while(!q.empty()){ready_queue.push(q.front());q.pop();}wait_map.erase(event);}}voidrun(){while(!ready_queue.empty()){Coroutine*c=ready_queue.front();ready_queue.pop();c->resume();if(c->finished)deletec;}}};//等待事件的宏#defineSCHED_YIELD_WAIT(sched,event)do{\(sched)->wait_for(this,event);\state=__LINE__;return;case__LINE__:;\}while(0)//模拟网络请求的`协程`structNetReader:Coroutine{Scheduler*sched;constchar*name;NetReader(Scheduler*s,constchar*n):sched(s),name(n){}protected:voidstep()override{CORO_BEGIN();printf("[%s] 开始,等待IO事件...\n",name);SCHED_YIELD_WAIT(sched,1);//等待事件1(模拟IO)printf("[%s] IO事件到达,处理完成.\n",name);CORO_END();}};intmain(){Scheduler scheduler;scheduler.spawn(newNetReader(&scheduler,"请求A"));scheduler.spawn(newNetReader(&scheduler,"请求B"));printf("分发器启动,所有`协程`将挂起等待IO...\n");scheduler.run();//`协程`执行到`SCHED_YIELD_WAIT`后挂起printf("\n--- 外部IO事件到达 ---\n");scheduler.notify(1);//唤醒所有等待事件1的`协程`printf("\n分发器再次运行,处理已就绪的`协程`...\n");scheduler.run();//执行后续逻辑return0;}

这段代码就是一套缩微的事件驱动协程框架.函数模拟外部世界,先运行,挂起协程;然后通知,模拟IO事件;再运行,唤醒协程继续执行.

这,就是epoll/kqueue这类IO多路复用结合协程底层原理.

三,宏的坑与C++20的春天

上面的这套宏,虽然讲清了原理,但有坑.
比如不能在子函数yield,RAII也容易失效.

幸好C++20带来了语言级的co_await.上面的NetReader,用C++20如下写出来.

#include<iostream>#include<coroutine>#include<thread>structTask{/*...样板...*/};structawaitable{/*...样板...*/};Tasknet_reader_cpp20(constchar*name){std::cout<<"["<<name<<"] Started, waiting for IO..."<<std::endl;co_awaitawaitable{};//`编译器`在此生成挂起点std::cout<<"["<<name<<"] IO received, finished."<<std::endl;}//`主`函数示意intmain(){net_reader_cpp20("Reader C++20 A");...实际需要一个分发器来驱动}

一个co_await``关键字,编译器就帮你做了之前用宏做的所有脏活累活,而且做得更好.但你只有亲手用宏撸一遍,才能真正理解co_await背后,编译器到底帮你省了多大的事.

软件干到头就是硬件,但把软件自身压榨到极限,你就是别人口中的大神.

今天从一个最简单的宏,一步步构建出一个有模有样的协程分发器,最后看到了C++20的方案.这整个过程,比你直接去背C++20``协程的八股文要有值得多.
我把协程的底裤给你扒了,但怎么穿上它,在什么场景下穿,就是你的修行了.
在你的项目里,哪个环节最适合协程来改造?
是网络IO,还是文件读写,或是一些复杂的业务流程状态机呢?

http://www.jsqmd.com/news/452756/

相关文章:

  • 如何快速搭建简单SpringBoot项目网页
  • 如何使用 Python 连接 MySQL 数据库?
  • 如何在docker中的mysql容器内执行命令与执行SQL文件
  • Flutter 组件 postgres_crdt 的适配 鸿蒙Harmony 实战 - 驾驭分布式无冲突复制数据类型、实现鸿蒙端高性能离线对等同步架构方案
  • 基于Java+SSM+Django大学生成果登记系统(源码+LW+调试文档+讲解等)/大学生成果登记系统使用教程/大学生成果登记平台/大学生成果管理系统/大学生成果申报系统/大学生成果展示系统
  • 基于Java+SSM+Django健身中心管理系统(源码+LW+调试文档+讲解等)/健身中心管理软件/健身房管理系统/健身中心会员管理系统/健身房会员软件/健身房管理软件/健身俱乐部管理系统
  • Flutter 组件 t_stats 的适配 鸿蒙Harmony 实战 - 驾驭高性能统计学运算、实现鸿蒙端海量数据实时态势感知与工业级描述性统计方案
  • 在SpringBoot项目中集成MongoDB
  • 地址转坐标:利用高德API进行批量地理编码
  • 基于Java+SSM+Flask网页商城系统(源码+LW+调试文档+讲解等)/网页商城系统使用教程/网页商城系统开发/网页商城系统模板/网页商城系统源码/网页商城系统搭建/网页商城系统优势
  • Flutter 组件 http_retry 的适配 鸿蒙Harmony 深度进阶 - 驾驭分布式负载感知重试、实现鸿蒙端高可靠通讯与协议幂等性审计方案
  • Flutter 组件 mock_client 的适配 鸿蒙Harmony 实战 - 驾驭 HTTP 协议级测试模拟、实现鸿蒙端离线环境下的接口断言与质量门禁方案
  • 某外包全员降薪,AI编程概念还没落地,外包程序员先背了锅,说句扎心的,这只是开始
  • Flutter 组件 shared_aws_api 的适配 鸿蒙Harmony 实战 - 驾驭跨平台 AWS 云服务通讯、实现鸿蒙端签名版本 4 (SigV4) 自动审计与高性能 API 鉴权方案
  • Flutter 组件 simplify 的适配 鸿蒙Harmony 实战 - 驾驭路径精简算法、实现鸿蒙端高性能地理足迹渲染与矢量图形优化方案
  • Flutter 组件 lcov_parser 的适配 鸿蒙Harmony 实战 - 驾驭 0307 批次代码质量审计、实现鸿蒙端测试覆盖率分析与自动化治理看板方案
  • Edge浏览器STATUS_ACCESS_DENIED错误终极指南:从原因分析到一键修复
  • ChatGPT指令实战指南:从基础到高级应用的最佳实践
  • 如何安全留存社交记忆?GetQzonehistory全攻略
  • 内容访问技术解析:开源工具辅助资源获取系统指南
  • x64dbg调试器实战:从零开始分析32位程序的完整流程(附常见问题解答)
  • 4步掌握开源内容解锁工具:突破付费访问限制的完整方案
  • 数字记忆备份完整指南:3个维度构建个人数据安全防线
  • 网页内容访问优化指南:合法获取付费内容的技术策略
  • 3步搞定音乐获取:让Spotify音乐实现永久离线自由
  • AI 辅助开发实战:基于 Spring Boot + Vue 的毕业设计高效构建与参考文献整合指南
  • PADS差分对设置避坑指南:为什么你的高速信号总是不稳定?
  • 【多目全景】基于海思3403平台的实时4路视频拼接与畸变校正技术解析
  • 客服智能体的运行图:从架构设计到性能优化实战
  • Flutter 三方库 brick_offline_first_with_rest 的鸿蒙化适配指南 - 打造离线优先架构、SQL与REST多端数据同步及一致性保障实战