嵌入式开发系列教程(六) 并发模型之多线程

对于学习,我有一个观点,便是学习一项技能,就要学习它的历史,这样才能搞清楚他的来龙去脉,理解他当下为什么是这个样子。

对于嵌入式编程来讲更是这样,单片机性能相当于早期的计算机,嵌入式程序员所面临的环境相当于早期的计算机程序员。我们可以从那里获得较好的经验。

在前面,我们探讨了回调和协程两种并发模型。对于复杂程序,回调模型实现困难,可读性不高,为此我们引入了协程模型。

我们还是沿用上一节的例子,对于一个LED,先点亮3s,再熄灭5s,然后点亮5s,再熄灭3s。我们希望得到的代码是样的

void LedFlash()
{
    LedOn();
    delay(3); 
    LedOff();
    delay(5);  
    LedOn();
    delay(5);   
    LedOff();
    delay(3);   
}

在上一节,我们利用协程模型得到了这样的代码

void LedFlash()
{
    Begin();
    LedOn();
    setTimerEvent(3000,LedFlash); //定时3s,3s后定时器系统再调用LedFlash函数
    Yield();     //yield,让出CPU,对应代码我们可以看到是return返回
    LedOff();  //3s后,LedFlash函数会根据state直接执行这一行  
    setTimerEvent(5000,LedFlash);  
    Yield();     //yield,让出CPU 
    LedOn();    
    setTimerEvent(5000,LedFlash);
    Yield();     //yield,让出CPU
    LedOff();   
    setTimerEvent(3000,LedFlash);
    Yield();     //yield,让出CPU
    End();

}

对比两段代码,我们会发现,协程模型多了Begin、setTimerEvent、Yield、End等函数语义。
同时,我们可以观察到,线程模型的delay函数实现了Yield、setTimerEvent语义。

也就是说,delay函数,让出了CPU,并会在定时结束后,自动回到该函数继续执行。这是怎样实现的呢?

void delay(int ms){
    setTimerEvent(ms,LedFlash);  //delay函数需要‘记得’在延时到期后执行delay后的语句
    Yield();     //yield,让出CPU //让出CPU,协程模型时我们是用的return呢?
}
  • delay函数需要‘记得’在延时到期后执行delay后的语句,这应该怎样实现呢?
  • 让出CPU,在协程模型中我们用的return,可在这里应该怎样实现呢?

我们再单片机的C语言一节中,讲述了单片机的C语言模型。这里我们再回忆一遍。

  • 单片机上有PC寄存器,记录指令地址,用于取指,有SP寄存器用于记录栈。
  • CPU在执行指令期间,会产生一些临时数据,一般都放置在栈,堆、bss空间。
  • 我们把上述CPU执行过程中用到的资源,称作运行上下文。

这样我们便可以得到一个结构体,记录上下文

struct context{
    int pc;
    int sp;
    void *begin_bss;
    void *end_bss;
    void *heap; 
    ....
};
  • 需要有一个当前上下文 struct context *current,记录当前执行序列的栈,堆,bss段等空间
  • 一个线程便是一个指令执行序列,需要一个上下文
  • 线程切换,便是将就绪线程的上下文机构,赋值给当前上下文结构,然后,执行PC跳转。
  • struct context相当于模拟了单片机的寄存器,内存等基本硬件。一个结构对应一套资源,只不过缺少CPU,对于CPU的复用,得从时间上来看,1ms执行序列(线程)A,1ms执行序列(线程)B,这样对于1s这个时段来说,A、B是同时执行的,他们共享了CPU。
  • 上下文切换需要对相当多的内容进行赋值,消耗是比较大的。
void delay(int ms){
    Yield();     //这里可以是 线程切换
    setTimerEvent(ms,LedFlash);  //再切换后来时,PC指针记录了当前位置,知道该执行哪条指令

delay函数让出了CPU,这是一个主动过程,对于多线程来讲,任务切换是需要调度器的。

  • 调度器后台执行,记录所有线程。
  • 利用定时器,实现一个100ms的定时器,我们称为时钟滴答,定时到了,遍历所有线程,检测该执行哪一个。
  • delay这类函数,会主动让出CPU,完成任务切换
  • 调度器,会根据某个线程的执行时间,优先级来对其进行调度。强制完成线程切换。

到这里,我们还有一个东西没有说,便是线程的同步。不过根据这篇文章,你应该对线程有了一个基本的认识,线程同步是个复杂的问题,需要的是更多的思考和练习,这里便不多做介绍了。

这是一个免费,开源的教程,如果你喜欢可以转发,也可以打赏奖励。 欢迎关注微信公众号小站练兵

嵌入式开发系列教程(六) 并发模型之多线程_第1张图片

你可能感兴趣的:(嵌入式开发系列教程(六) 并发模型之多线程)