吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 14352|回复: 35
收起左侧

[系统底层] 手把手实现一个协程/时间片轮转调度

  [复制链接]
JuncoJet 发表于 2019-11-8 14:50
前言
其实是这样的,前不久突发奇想的想实现一个动画时钟。用的是经典的动画架构,两个线程分别负责渲染和数据运算。
但是呢,如果需要移植到单片机或者其他的低级环境(没有多线程),只能重新实现。不过还有一个办法,就是使用协程。

协程是啥(内容直接 C-c,C-v)
协程(Coroutine)编译器级的,进程(Process)和线程(Thread)操作系统级的
进程(Process)和线程(Thread)是os通过调度算法,保存当前的上下文,然后从上次暂停的地方再次开始计算,重新开始的地方不可预期,每次CPU计算的指令数量和代码跑过的CPU时间是相关的,跑到os分配的cpu时间到达后就会被os强制挂起,开发者无法精确的控制它们。
协程(Coroutine)是一种轻量级的用户态线程,实现的是非抢占式的调度,即由当前协程切换到其他协程由当前协程来控制。目前的协程框架一般都是设计成 1:N 模式。所谓 1:N 就是一个线程作为一个容器里面放置多个协程。那么谁来适时的切换这些协程?答案是有协程自己主动让出 CPU,也就是每个协程池里面有一个调度器,这个调度器是被动调度的。意思就是他不会主动调度。而且当一个协程发现自己执行不下去了(比如异步等待网络的数据回来,但是当前还没有数据到),这个时候就可以由这个协程通知调度器,这个时候执行到调度器的代码,调度器根据事先设计好的调度算法找到当前最需要 CPU 的协程。切换这个协程的 CPU 上下文把 CPU 的运行权交个这个协程,直到这个协程出现执行不下去需要等等的情况,或者它调用主动让出 CPU 的 API 之类,触发下一次调度。

时间片轮转调度
我不知道这是啥啊,噗,只是听上去很帅的样子。和协程相似,应用于底层(操作系统)的话也许名字就叫这个吧。

实现协程的方式
平时的话,其实有很多选择的嘛。比如说 Arduino 玩家会常玩的 ProtoThreads,比较轻量级,使用的 C 语言宏写成,占用资源低,易于移植。还有Libco啥的,其实没用过(捂脸。主要是如果无法理解协程的话,用起来会不怎么得(dei第三声)力。和线程以及RTOS的多任务不同,程序执行过程中不会中断去执行另一个任务。如果阻塞的一个协程的话,可能导致程序无法正常的表达。
自己实现一个协程的话,有助于理解底层原理,以及了解他的弊端。在以后的使用中,可以规避掉这种不正确的使用,写出更优质的代码。

TIM截图20191108134814.jpg
我画了个示意图,这个是我个人的理解啊,我也不保证是完全对的。欢迎拍板砖。

根据上面的图,我们可以这样设计程序。
Image 447.jpg
代码实现的是两个print字符串的过程,一个是1秒一个是2秒。类似多线程的效果。可以看到实现的功能很OjbK。之后,可以应用此协程库,来写点厉害的东西。
(本协程库已开源 https://github.com/JuncoJet/simCoroutines

CliClock.gif
(动画帧数低,将就看,实际效果比这好多了)
这个是前些时候写的Cli的动画时钟,前言中提到的。我已经分别用多线程和自己写的协程来重新实现过了。两个版本都会打包,稍后可以附件中下载。

Image 449.jpg

Image 448.jpg
代码写的比较像Arduino中的代码,这样写有种亲切感,开开开玩笑。主要程序一旦运行就不会退出,结构很相似吧,所以就做了个模仿秀。

tgt[3]分别存储了3个协程触发的时间,程序loop中做时间轮询,时间到就触发代码块。
[C] 纯文本查看 复制代码
void loop(){//负责时间片轮转 
DWORD m=timeGetTime();
if(m>=tgt[0]||!tgt[0])showTime();//协程0,显示时间
if(m>=tgt[1]||!tgt[1])getTime();//协程1,获取时间
if(m>=tgt[2]||!tgt[2])change();//协程2,递增数值
Sleep(1);
}


delay确定了下一次的执行时间,
[C] 纯文本查看 复制代码
void delay(int t,int x){//负责更新时间 
	tgt[x]=(tgt[x]?tgt[x]:timeGetTime())+t;
}

第一次运行tgt为空的话直接用timeGetTime()的值,之后直接累加。如果继续使用timeGetTime()的话会被协程执行时间延误从而受到影响,为避免协程运行时间产生偏差,所以我采用直接累加的方式。
其他功能,下载附件后自行研究吧,本章主讲协程。这个代码只是举个栗子!

协程的好处
协程有三宝,双手和大脑(雾
协程占用资源低,可以应用于一些环境苛刻的地方,如大多数的8位单片机。
协程是一种(新的)程序的思想,单线程写着无聊了,可以尝试另一种写法。
协程还能应用于VB、Ruby(不黑你的假多线程)之类的无法完美支持多线程的程序中使用。

内容比较干,比较初级,欢迎大家留言深入讨论。

CliClock.zip

14.52 KB, 下载次数: 29, 下载积分: 吾爱币 -1 CB

免费评分

参与人数 6吾爱币 +9 热心值 +5 收起 理由
Pshmily520 + 1 谢谢@Thanks!
wkgczx + 1 + 1 热心回复!
demo886 + 1 用心讨论,共获提升!
bestnike + 1 + 1 我很赞同!
Hmily + 6 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
泳诗 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

小可爱~ 发表于 2019-12-5 19:45
JuncoJet 发表于 2019-12-5 10:32
协程没必要汇编,抢占式的才需要汇编写。估计只是爱好而已。

我还是不理解楼主代码中的协程关键的地方, 这里是我想请教的地方, 虽然说子函数调用也是一种特殊的协程, 但是我理解的协程的主要目的是我在执行
[C++] 纯文本查看 复制代码
funcA(){
   a1;
   a2;
   a3;
}
funcB(){
   b1;
   b2;
   b3;
}

的时候, 当一个线程执行到 a1 然后执行 a2 的时候突然切换到 funcB 执行 b1 b2 忽然有回到 funcA再执行a3执行完毕后, 这个线程回到 funcB 执行 b3, 这种切换不是通过函数调用的, 而是线程自动切换的, 这是我所理解的协程, 跟多线程的生产者和消费者模型差不多, 但是前面的是多线程, 而协程可以使用单线程可以完成这件事情, 今天看到楼主这种方式实现的协程我有点蒙了, 所以想跟楼主探讨探讨
小可爱~ 发表于 2019-12-5 09:52
JuncoJet 发表于 2019-12-5 08:57
协程不需要汇编的,switch case 之类的也可以实现

不太懂, 可能我不理解什么是协程吧, 是买票的还是酒店服务的吧
c/c++基于汇编实现的协程方法
我只记得这位大佬在某群发布过协程底层实现的部分代码, 使用的就是汇编而且是几十行的那种
TIM图片20191205094809.png


TIM截图20191205094529.png
朱朱你堕落了 发表于 2019-11-8 15:42
 楼主| JuncoJet 发表于 2019-11-8 15:45
朱朱你堕落了 发表于 2019-11-8 15:42
大佬。这是什么编译器?没见过

C-Free(MinGW)啊
apples1949 发表于 2019-11-8 15:51
膜拜膜拜
q74330 发表于 2019-11-8 15:52
楼主小哥想表达什么,,看了半天,,小哥要照顾小白
wakichie 发表于 2019-11-8 17:04
膜拜大佬啊
bester 发表于 2019-11-8 19:00
大佬啥时候放点黑科技出来呀
lep52 发表于 2019-11-9 06:58
学习一下,虽然看不懂
章学诚 发表于 2019-11-9 11:07
大师级别,赞赞赞,学习了
露哥爱破解 发表于 2019-11-10 09:14
看不懂 觉得很高大上
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-27 03:47

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表