解决程序堵塞的优化方法(一)

简介: 解决程序堵塞的优化方法(一)

     刚开始学习编写嵌入式的代码,例如在单片机上控制LED灯500ms亮灭,因为属于初学,对于MCU运行效率没有要求,所以大部分的教程都是delay_ms(500)。平时在一些基本调试中,对我们影响很小。但是我们需要了解,当程序使用大量类似delay形式的函数时,会对程序造成严重阻塞。


        以delay延时函数为例:一般写程序,都是通过while()或者for()加上条件判断,进行循环累加,如果延时函数执行过程中,有中断响应执行,但是当中断服务函数执行完毕,程序跳回后台主循环执行的时候,由于延时函数里面是条件循环,在条件没有符合要求的时候,程序将一直堵塞到延时函数,无法执行响应中断处理函数的次级函数。

   e.g. 曾经在学校项目中,在控制电机的程序中对于匀加速匀减速的部分添加了delay函数,实际设备运行起来后,别说匀加/减速,就是加速减速变化都被阻塞严重,严重影响了操作的体验。所以下面介绍一下我操作的几种解决阻塞的程序实现。

/*while形式*/
void Delay_ms(u16 nms)
{         
  while(nms--)
  {
    u32 temp;       
    SysTick->LOAD=(u32)1*fac_ms;        
    SysTick->VAL =0x00;              
    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;  
    do
    {temp=SysTick->CTRL;
    }while((temp&0x01)&&!(temp&(1<<16)));    
    SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;  
    SysTick->VAL =0X00;                 
  }
}
/*for 形式*/
void delay_ms(void)
/{
   for(u8 i =0;i<230;i++)
    for(int j =0;j<320;j++);
}

总述

1.通过运用RTOS,线程里面调用系统提供的延时等函数,实现解决阻塞。
2.全局变量方法,定时器计数条件判断替代延时。
3.使用状态机方式,分解动作,替代延时实现。
4.使用链表式,与第三种相似。

一、 RTOS方法

     诚然现在RTOS很火,大家可以使用各种实时操作系统,利用优化后的delay函数,这个时候我们可以随便调用delay而不用担心阻塞,因为在RTOS中,在线程使用系统提供的delay函数,线程会被内核挂起,系统会自动切换到其他优先级别更高的线程运行。但是一定记住不是所有程序都适合RTOS。

   参照其他人的博文:“如果程序任务可折分性较差,折分后的各个任务之间有 N 多的同步问题和复用资源问题,此时不要用多任务操作系统,我们不能因为任务而任务,任务多并没有想象的好,多任务是用降低实时性来换取软件开发的独立性,不要被实时多任务操作系统的实时两个字骗了,这个实时只是相对于其它非实时性多任务操作系统来讲的,实时性最高的当然是你自己编写的单任务程序。”

   RTOS的延时代码做了底层的变化,和我们写的while等循环代码有很大差别。RTOS延时函数进入之后会将需要延时的线程挂起进入阻塞态,底层时钟节拍计数器进行定时,直至预约的延时时间到来,再利用任务调度器唤醒线程,线程按照RTOS的优先级执行。

1.RT_Thread 延时代码部分

4edc953e2c684bbe819ffa954c899c08.png4edc953e2c684bbe819ffa954c899c08.png

2.U/COS III 延时代码部分

4edc953e2c684bbe819ffa954c899c08.png4edc953e2c684bbe819ffa954c899c08.png

3.FreeRTOS 延时代码部分


4edc953e2c684bbe819ffa954c899c08.png4edc953e2c684bbe819ffa954c899c08.png

各大RTOS官网资料

可以按照“RTOS”关键词自行搜索,包括社区资料,源码以及开发手册的提供。下面展示国产操作系统rt_thread官网界面。

4edc953e2c684bbe819ffa954c899c08.png

正点原子与野火等资料

可以按照”正点原子“关键词自行搜索,包括正点原子的BBS以及各大视频网站都有其资料。

4edc953e2c684bbe819ffa954c899c08.png4edc953e2c684bbe819ffa954c899c08.png

一、 全局变量法

      全局变量的方法其实与时间管理的思路很像,将设置变量放置于定时器,然后进行计数器累加,再在主循环或者可以进行反复触发的函数中,通过对此变量数值的判断,从而实现等量delay函数的执行。实现方法也很简单,定义一个变量放置于定时器即可,剩下的通过变量值除以间隔时间来判断时间长短,再进行执行相应的函数。

   虽然此种方法比较简单,但是需要大家注意,通过设置全局变量法进行替代delay函数,只能放置于主循环或者多触发的程序执行部分,如果放置在初始化的部分,则没有实际用处。

代码实现部分 ->注释部分需要重点观看
/*定义一个全局变量*/
static u32 G_timer = 0;
/*定时器的时间间隔由用户自己把握,此处为了计算方便,定时器间隔设置为 1ms*/
void TIM_IRQ(void)
{
  G_timer++;
  if(G_timer % 100 == 0)/*计数器每 100ms 进入执行*/
  {
    taskfun();/*此处函数不可以执行复杂运算,只可以实现简单的IO控制,
              以及一些基本的变量赋值、加减的。
              如果程序过于复杂,会导致此处中断服务函数造成堵塞*/
  }
}
/*主程序*/
int main(int argc,char** argv)
{
  SystemInit();
  static u32 lastcnt1 =0 ,lastcnt2 =0 ;
  while(true)
  {
    if(G_timer % 100 == 0)/*此处不能使用如此函数执行,因为主循环中,
                          一般晶振频率很高,程序在主循环中执行的间隔小于
                          计数器执行的频率,也就是计数器计一个数,程序
                          可能执行多次。所以这种方法不能使用,建议使用下面的方法*/
    {
      taskfun1();
    }  
    if(G_timer % 1000 == 0)/*计数器每 1000ms或者叫1s 进入执行*/
    {
      taskfun2();/*此处可以执行复杂的运算,不需要担忧阻塞*/
       lastcnt1 = G_timer ;
    } 
    if(G_timer % 10000 == 0)/*计数器每 10000ms或者叫10s 进入执行*/
    {
      taskfun3();/*注释与taskfun2();相同*/
      lastcnt2 = G_timer ;            
    } 
  }
}

    到现在介绍了两种方法,因为篇幅有限,过长的文字不适合大家一次性阅读,我会在下一篇文章继续分享,解决堵塞的第三种及第四种方法,希望可以帮助到大家,谢谢。

目录
相关文章
|
21天前
|
Arthas Java 应用服务中间件
我的程序突然罢工了|深入探究HSF调用异常,从死锁到活锁的全面分析与解决
本文详细记录了作者在处理HSF调用异常问题的过程中,从初步怀疑死锁到最终发现并解决活锁问题的全过程。
213 13
|
2月前
|
数据采集 存储 NoSQL
提高爬虫性能的 5 个关键技巧:从并发到异步执行
本文介绍了提高网络爬虫性能的五个关键技巧:并发请求、异步执行、使用代理IP、限制请求频率与休眠时间、优化数据提取与存储。结合拼多多的实际案例,展示了如何通过这些技术优化爬虫效率,确保数据采集的高效性和稳定性。
262 0
|
5月前
|
机器学习/深度学习 Java 数据挖掘
线程操纵术之更优雅的并行策略问题之并发和并行有区别问题如何解决
线程操纵术之更优雅的并行策略问题之并发和并行有区别问题如何解决
|
5月前
|
安全
线程操纵术并行策略问题之ForkJoinTask提交任务的问题如何解决
线程操纵术并行策略问题之ForkJoinTask提交任务的问题如何解决
|
6月前
|
缓存 算法 Java
深入解析线程上下文切换的原理与优化策略
深入解析线程上下文切换的原理与优化策略
581 0
|
7月前
|
监控 安全
线程死循环是多线程应用程序开发过程中一个难以忽视的问题,它源于线程在执行过程中因逻辑错误或不可预见的竞争状态而陷入永久运行的状态,严重影响系统的稳定性和资源利用率。那么,如何精准定位并妥善处理线程死循环现象,并在编码阶段就规避潜在风险呢?谈谈你的看法~
避免线程死循环的关键策略包括使用同步机制(如锁和信号量)、减少共享可变状态、设置超时、利用监控工具、定期代码审查和测试、异常处理及设计简洁线程逻辑。通过这些方法,可降低竞态条件、死锁风险,提升程序稳定性和可靠性。
106 0
|
缓存 Java 容器
【并发技术10】线程并发库的使用
【并发技术10】线程并发库的使用
|
Java 编译器 调度
锁的优化过程
锁的优化过程
|
网络协议 PHP 数据库
浅谈非堵塞程序的理解
当程序需要发送网络请求或者从磁盘中读取文件等IO操作时 CPU发出指令,然后信号经过总线到达网卡或者磁盘 然后拿到数据,再经过总线到达主存中,CPU继续对主存中的数据进行操作。
162 0
浅谈非堵塞程序的理解
解决程序堵塞的优化方法(二)
解决程序堵塞的优化方法(二)
184 0
解决程序堵塞的优化方法(二)