前言
其实是这样的,前不久突发奇想的想实现一个动画时钟。用的是经典的动画架构,两个线程分别负责渲染和数据运算。
但是呢,如果需要移植到单片机或者其他的低级环境(没有多线程),只能重新实现。不过还有一个办法,就是使用协程。
协程是啥(内容直接 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的多任务不同,程序执行过程中不会中断去执行另一个任务。如果阻塞的一个协程的话,可能导致程序无法正常的表达。
自己实现一个协程的话,有助于理解底层原理,以及了解他的弊端。在以后的使用中,可以规避掉这种不正确的使用,写出更优质的代码。
我画了个示意图,这个是我个人的理解啊,我也不保证是完全对的。欢迎拍板砖。
根据上面的图,我们可以这样设计程序。
代码实现的是两个print字符串的过程,一个是1秒一个是2秒。类似多线程的效果。可以看到实现的功能很OjbK。之后,可以应用此协程库,来写点厉害的东西。
(本协程库已开源 https://github.com/JuncoJet/simCoroutines)
(动画帧数低,将就看,实际效果比这好多了)
这个是前些时候写的Cli的动画时钟,前言中提到的。我已经分别用多线程和自己写的协程来重新实现过了。两个版本都会打包,稍后可以附件中下载。
代码写的比较像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(不黑你的假多线程)之类的无法完美支持多线程的程序中使用。
内容比较干,比较初级,欢迎大家留言深入讨论。 |