- #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线程,这个场景是经常会使用到的,所以希望可以优化这个功能!