[程序源码]你是否也被蒙在鼓里?RTThread真正暂停线程的方法

1.介绍
本以为暂停线程是一条语句的事,结果发现事情并没有那么简单,而且我相信很多人都用错了,就是因为踩了这么一个坑,所以准备发这么一个帖子,让后面的人少踩坑。
2.应用场景
有三个线程,分别为A、B和C,把A线程比作是大脑,B和C线程分别是扫地和洗碗,首先处于空闲状态,大脑让我去扫地,这时A线程中会开启B线程,第一次开启线程可以调用【rt_thread_startup】这个函数,没啥毛病,当老婆大人让我去洗碗,大脑瞬间做出反应去洗碗,这时A线程会关闭B线程然后打开C线程,那么A线程关闭B线程是调用【rt_thread_suspend】这个函数吗?看着没啥问题哦,但是里面存在很大的问题!这里先不说,继续往下走,当洗完碗还得继续扫地,让A线程恢复B线程需要调用【rt_thread_resume】这个函数。
上面这个应用场景就实现了,A线程暂停和恢复B线程,看着是没什么问题,我们先来实际操作一下,看看有什么问题!
3.场景再现
硬件:STM32F407VET6
RTThread版本:3.1.3Nano
首先按照上面的应用场景走一遍,设计三个线程,分别为A、B和C,然后开始运行,具体代码如下:

#include <rtthread.h>



ALIGN(RT_ALIGN_SIZE)

static rt_uint8_t a_thread_stack[ 512 ];



ALIGN(RT_ALIGN_SIZE)

static rt_uint8_t b_thread_stack[ 512 ];



ALIGN(RT_ALIGN_SIZE)

static rt_uint8_t c_thread_stack[ 512 ];



static struct rt_thread a_thread;

static struct rt_thread b_thread;

static struct rt_thread c_thread;



static void a_thread_entry(void *parameter)

{

    unsigned int count = 0;



    while (1)

    {

        count++;

        if(count == 10)

        {

            rt_kprintf("b start!\n");

            rt_thread_startup(&b_thread);   //开始扫地

        }

        else if(count == 30)

        {

            rt_kprintf("b pause!\n");

            rt_thread_suspend(&b_thread);   //停止扫地

            rt_kprintf("c start!\n");

            rt_thread_startup(&c_thread);   //开始洗碗

        }

        else if(count == 50)

        {

            rt_kprintf("c pause!\n");

            rt_thread_suspend(&c_thread);   //停止洗碗

            rt_kprintf("b start!\n");

            rt_thread_resume(&b_thread);    //开始扫地

        }

        rt_thread_delay(100);

    }

}



static void b_thread_entry(void *parameter)

{

    unsigned int count = 0;



    while (1)

    {

        rt_kprintf("b thread run!\n");

        rt_thread_delay(300);

    }

}



static void c_thread_entry(void *parameter)

{

    unsigned int count = 0;



    while (1)

    {

        rt_kprintf("c thread run!\n");

        rt_thread_delay(100);

    }

}



int pause_thread_init(void)

{

    rt_err_t result;



    /* init led thread */

    result = rt_thread_init(&a_thread,

                            "a_thread",

                            a_thread_entry,

                            RT_NULL,

                            (rt_uint8_t *)&a_thread_stack[0],

                            sizeof(a_thread_stack),

                            5,

                            5);

                            

    if (result == RT_EOK)

    {

        rt_thread_startup(&a_thread);

    }

    

    /* init led thread */

    result = rt_thread_init(&b_thread,

                            "b_thread",

                            b_thread_entry,

                            RT_NULL,

                            (rt_uint8_t *)&b_thread_stack[0],

                            sizeof(b_thread_stack),

                            6,

                            5);

                            

    

    /* init led thread */

    result = rt_thread_init(&c_thread,

                            "c_thread",

                            c_thread_entry,

                            RT_NULL,

                            (rt_uint8_t *)&c_thread_stack[0],

                            sizeof(c_thread_stack),

                            7,

                            5);

                            

    return 0;

}



/* 导出到 msh 命令列表中 */

MSH_CMD_EXPORT(pause_thread_init, pause thread);

来看看实际的效果,如下图1所示,可以发现调用【rt_thread_suspend】函数并没有真正停止线程B或线程C。

图1

 这是为什么呢?我们上RTThread官网看一下,观看图2可以发现【rt_thread_suspend】的函数说明中,特意的说明了这个函数不能通过A线程挂起B线程。

图2

同时我也上网查了一下,大家都有这个疑问,然后我们翻开源码看一下,在这个函数中有这么一个判断,会判断需要挂起线程的状态,如果线程不等于就绪态那么就不会进入挂起,而sleep,delay等函数都会导致线程的挂起,那么其他线程想要挂起这个线程肯定会掉到这个if里面,从而挂起不了这个线程。

图3

4.解决方案
可以使用删除线程和创建线程的方式来停止线程的运行,但是这个对线程有一定的要求,比如线程没有运行时长的变量,如果有的话,线程删除了这个变量的值也会被复位,要么这个变量设置为全局变量,但是这样就不太合适,代码实现如下所示:

#include <rtthread.h>



ALIGN(RT_ALIGN_SIZE)

static rt_uint8_t a_thread_stack[ 512 ];



ALIGN(RT_ALIGN_SIZE)

static rt_uint8_t b_thread_stack[ 512 ];



ALIGN(RT_ALIGN_SIZE)

static rt_uint8_t c_thread_stack[ 512 ];



static struct rt_thread a_thread;

static struct rt_thread b_thread;

static struct rt_thread c_thread;



static void b_thread_entry(void *parameter);

static void c_thread_entry(void *parameter);



static void a_thread_entry(void *parameter)

{

    unsigned int count = 0;



    while (1)

    {

        count++;

        if(count == 10)

        {

            rt_kprintf("b start!\n");

            /* init led thread */

            rt_thread_init(&b_thread,

                            "b_thread",

                            b_thread_entry,

                            RT_NULL,

                            (rt_uint8_t *)&b_thread_stack[0],

                            sizeof(b_thread_stack),

                            6,

                            5);

            rt_thread_startup(&b_thread);   //开始扫地

        }

        else if(count == 30)

        {

            rt_kprintf("b pause!\n");

            rt_thread_detach(&b_thread);    //停止扫地

            rt_kprintf("c start!\n");

            rt_thread_init(&c_thread,

                            "c_thread",

                            c_thread_entry,

                            RT_NULL,

                            (rt_uint8_t *)&c_thread_stack[0],

                            sizeof(c_thread_stack),

                            7,

                            5);

            rt_thread_startup(&c_thread);   //开始洗碗

        }

        else if(count == 50)

        {

            rt_kprintf("c pause!\n");

            rt_thread_detach(&c_thread);    //停止洗碗

            rt_kprintf("b start!\n");

            rt_thread_init(&b_thread,

                            "b_thread",

                            b_thread_entry,

                            RT_NULL,

                            (rt_uint8_t *)&b_thread_stack[0],

                            sizeof(b_thread_stack),

                            6,

                            5);

            rt_thread_startup(&b_thread);   //开始扫地

        }

        rt_thread_delay(100);

    }

}



static void b_thread_entry(void *parameter)

{

    unsigned int count = 0;



    while (1)

    {

        rt_kprintf("b thread run!\n");

        rt_thread_delay(300);

    }

}



static void c_thread_entry(void *parameter)

{

    unsigned int count = 0;



    while (1)

    {

        rt_kprintf("c thread run!\n");

        rt_thread_delay(100);

    }

}



int pause_thread_init(void)

{

    rt_err_t result;



    /* init led thread */

    result = rt_thread_init(&a_thread,

                            "a_thread",

                            a_thread_entry,

                            RT_NULL,

                            (rt_uint8_t *)&a_thread_stack[0],

                            sizeof(a_thread_stack),

                            5,

                            5);

                            

    if (result == RT_EOK)

    {

        rt_thread_startup(&a_thread);

    }

                            

    return 0;

}



/* 导出到 msh 命令列表中 */

MSH_CMD_EXPORT(pause_thread_init, pause thread);

查看下图2,可以看到这个方式可以实现线程停止的功能,但是这种方式非常有局限性,而且开销比较大,因为需要不停的初始化和脱离线程,并且对任务中的变量有一定的要求,非常容易让代码产生BUG,一般不采纳。

图4

这里我推荐使用信号量的方法来暂停线程,首先定义一个暂停信号量,然后在需要暂停的线程中去不停的监测这个信号量,当接收到信号量时,自己主动挂起线程并让出CPU,这样就可以实现暂停线程,而且还能够知道线程暂停在哪。代码实现如下:

 

#include <rtthread.h>



ALIGN(RT_ALIGN_SIZE)

static rt_uint8_t a_thread_stack[ 512 ];



ALIGN(RT_ALIGN_SIZE)

static rt_uint8_t b_thread_stack[ 512 ];



ALIGN(RT_ALIGN_SIZE)

static rt_uint8_t c_thread_stack[ 512 ];



static struct rt_thread a_thread;

static struct rt_thread b_thread;

static struct rt_thread c_thread;



static rt_sem_t b_pause_sem = RT_NULL;

static rt_sem_t c_pause_sem = RT_NULL;



static void a_thread_entry(void *parameter)

{

    unsigned int count = 0;



    while (1)

    {

        count++;

        if(count == 10)

        {

            rt_kprintf("b start!\n");

            rt_thread_startup(&b_thread);   //开始扫地

        }

        else if(count == 30)

        {

            rt_kprintf("b pause!\n");

            rt_sem_release(b_pause_sem);//rt_thread_suspend(&b_thread);   //停止扫地

            rt_kprintf("c start!\n");

            rt_thread_startup(&c_thread);   //开始洗碗

        }

        else if(count == 50)

        {

            rt_kprintf("c pause!\n");

            rt_sem_release(c_pause_sem);//rt_thread_suspend(&c_thread);   //停止洗碗

            rt_kprintf("b start!\n");

            rt_thread_resume(&b_thread);    //开始扫地

        }

        rt_thread_delay(100);

    }

}



static void b_thread_entry(void *parameter)

{

    unsigned int count = 0;



    while (1)

    {

        rt_kprintf("b thread run!\n");

        rt_thread_delay(300);

        if(rt_sem_take(b_pause_sem, 0) == RT_EOK)

        {

            rt_thread_suspend(&b_thread);   //停止扫地

            rt_schedule();

        }

    }

}



static void c_thread_entry(void *parameter)

{

    unsigned int count = 0;



    while (1)

    {

        rt_kprintf("c thread run!\n");

        rt_thread_delay(100);

        if(rt_sem_take(c_pause_sem, 0) == RT_EOK)

        {

            rt_thread_suspend(&c_thread);   //停止扫地

            rt_schedule();

        }

    }

}



int pause_thread_init(void)

{

    rt_err_t result;

    

    b_pause_sem = rt_sem_create("b_pause", 0, RT_IPC_FLAG_PRIO);

    

    c_pause_sem = rt_sem_create("c_pause", 0, RT_IPC_FLAG_PRIO);



    /* init led thread */

    result = rt_thread_init(&a_thread,

                            "a_thread",

                            a_thread_entry,

                            RT_NULL,

                            (rt_uint8_t *)&a_thread_stack[0],

                            sizeof(a_thread_stack),

                            5,

                            5);

                            

    if (result == RT_EOK)

    {

        rt_thread_startup(&a_thread);

    }

    

    /* init led thread */

    result = rt_thread_init(&b_thread,

                            "b_thread",

                            b_thread_entry,

                            RT_NULL,

                            (rt_uint8_t *)&b_thread_stack[0],

                            sizeof(b_thread_stack),

                            6,

                            5);

                            

    

    /* init led thread */

    result = rt_thread_init(&c_thread,

                            "c_thread",

                            c_thread_entry,

                            RT_NULL,

                            (rt_uint8_t *)&c_thread_stack[0],

                            sizeof(c_thread_stack),

                            7,

                            5);

                            

    return 0;

}



/* 导出到 msh 命令列表中 */

MSH_CMD_EXPORT(pause_thread_init, pause thread);

运行结果如图5所示,可以正常暂停,当然这个办法也有一些问题,挂起线程会有延时,只要这个延时不影响程序的整体运行,这个方法还是非常不错的!

图5

5.总结
希望RTThread能出一些更好暂停线程的方法,上述的方法只能作为应急使用,并不是最优的解决方案,A线程暂停B线程,这个场景是经常会使用到的,所以希望可以优化这个功能。
---------------------
作者:二哲科技
链接:https://bbs.21ic.com/icview-3171470-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值