前言:砸在脑壳上的Protothreads
初见Protothreads
记得那是一个阳光明媚的下午,我百般无聊地阅读着小米的SDK,偶样一段这样的代码引起了我的注意。
我实在没有想明白PT这个单词的含义,本着刨根问底的精神。在查询百度之后,我学会了一个新名词 : protothreads。我似懂非懂的阅读着这个全部由宏写成的,磕磕碰碰地理解着另类的switch case的调度原理。就算是有百度在后见支持着,我还是难以得到其精髓。一怒之下,我突然灵光一闪,想起了程序预编译的过程,于是我在ubuntu上用gcc -E 把宏展开,这下不就一目了然了吗?
于是我决定把的代码进行展开
static PT_THREAD(demo_thread(struct pt *pt))
{
static uint8_t s_cond = PT_FALSE;
PT_BEGIN_EX(pt);
LOG("线程的开始了 ,while 1 在下面 demo thread \r\n");
while (1)
{
LOG("PT_YIELD 的前面 \r\n");
PT_YIELD(pt);
LOG("PT_YIELD 的后面 \r\n");
LOG("PT_YIELD_UNTIL 的前面:等待条件变真 \r\n");
PT_YIELD_UNTIL(pt, s_cond);
LOG("PT_YIELD_UNTIL 的后面:条件变真后进入 \r\n");
}
LOG("线程的结束 ,由于while 1 在上面 \r\n");
PT_END_EX(pt);
}
结果我得到了以下的代码
static char demo_thread(struct pt *pt)
{
static uint8_t s_cond = 0;
{
char PT_YIELD_FLAG = 1;
switch ((pt)->lc)
{
case 0:;
printf("线程的开始了 ,while 1 在下面 demo thread \r\n");
while (1)
{
printf("PT_YIELD 的前面 \r\n");
do
{
PT_YIELD_FLAG = 0;
(pt)->lc = 40;
case 40:;
if (PT_YIELD_FLAG == 0)
{
return 1;
}
}
while (0);
printf("PT_YIELD 的后面 \r\n");
printf("PT_YIELD_UNTIL 的前面:等待条件变真 \r\n");
do
{
PT_YIELD_FLAG = 0;
(pt)->lc = 44;
case 44:;
if ((PT_YIELD_FLAG == 0) || !(s_cond))
{
return 1;
}
}
while (0);
printf("PT_YIELD_UNTIL 的后面:条件变真后进入 \r\n");
}
printf("线程的结束 ,由于while 1 在上面 \r\n");
};
PT_YIELD_FLAG = 0;
(pt)->lc = 0;
;
return 3;
};
}
这回我终于看懂了,原理这个PT的任务调度是通过标记代码行数,跳转到对应的case 行数。再经过轮询的结构,从而实现一个任务调度。这个发现确实让我非常着迷,似乎看到一个与众不同的OS,而且这个OS是裸机的朋友来说这么的友好,对低资源的MCU是如此友好。但此时我能力并没有我萌发封装这个PT的,实现一个无堆栈的操作的想法。一番了解之后,再次把它放到一个角落里面。只留下了一个叫PT的协程,它的任务调度仅需要2个字节的记忆
再遇Protothreads
机遇巧合之下我再次在小米的SDK里面遇到了这个PT,这是多么熟悉的PT_BEGIN啊。这个时候的我已经不再是一个初出茅庐的程序员了,这几年来的陆陆续续使用过Free RTOS ,阅读过ucos等操作系统,再次看到这个PT的时候,我发现我可以具备让他成为一个OS的能力,我封装起来,让它运行Sleep!
这个想法一旦出现,我完全着迷了,是啊。我可以通过我的设计,让PT,具备OS的sleep功能,让他成为一个操作系统,解决资源少的MCU,更容易加入操作系统,让异步的业务可以使用同步的设计逻辑。
于是我在github上创建了一个仓库 https://github.com/liufuzhao/Protothreads.git
于是我在在CSDN上展开Protothreads的专栏。主要详解github上的Protothreads特性和设计方案已经使用建议。