美文网首页单片机学习程序员
嵌入式开发系列教程(六) 并发模型之多线程

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

作者: qianlihu | 来源:发表于2017-05-05 09:39 被阅读47次

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

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

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

    我们还是沿用上一节的例子,对于一个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,完成任务切换
    • 调度器,会根据某个线程的执行时间,优先级来对其进行调度。强制完成线程切换。

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

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

    相关文章

      网友评论

        本文标题:嵌入式开发系列教程(六) 并发模型之多线程

        本文链接:https://www.haomeiwen.com/subject/psyjtxtx.html