上一篇:RT-Thread线程管理教程 http://t.csdn.cn/phnwQ
目录
一、静态创建线程和动态创建线程的区别
RT-Thread是一个开源的嵌入式实时操作系统,支持静态创建线程和动态创建线程两种方式。
静态创建线程是在编译时就确定了线程的数量和属性,将线程的控制块和堆栈空间预分配好,然后在系统初始化时创建。静态创建线程的优点是创建速度快,不会产生动态内存分配的开销,适用于线程数量确定且固定的应用场景。
动态创建线程是在运行时根据需要动态地创建线程,可以根据不同的应用场景动态地调整线程的数量和属性。动态创建线程的优点是灵活性高,可以根据系统负载情况动态地创建或销毁线程,适用于线程数量不确定或者需要动态调整的应用场景。
总的来说,静态创建线程适用于线程数量确定且固定的应用场景,动态创建线程适用于线程数量不确定或者需要动态调整的应用场景。具体选择哪种方式,需要根据具体的应用场景和系统需求来进行权衡和选择。
二、创建线程的常用函数
线程创建函数,返回一个rt_thread_t类型:
rt_thread_create(const char *name, //线程名称
void (*entry)(void *parameter), //线程的入口函数
void *parameter, //入口函数的参数指针
rt_uint32_t stack_size, //线程堆栈的大小
rt_uint8_t priority, //线程优先级
rt_uint32_t tick) //线程调度的时间片大小
线程删除函数,传入创建函数的返回类型:
rt_thread_delete(rt_thread_t thread)
线程的初始化函数:
rt_thread_init(struct rt_thread *thread, //线程控制块指针
const char *name, //线程名称
void (*entry)(void *parameter), //线程的入口函数
void *parameter, //入口函数的参数指针
void *stack_start, //线程堆栈的起始地址
rt_uint32_t stack_size, //线程堆栈的大小
rt_uint8_t priority, //线程的优先级
rt_uint32_t tick) //线程调度的时间片大小
脱离线程函数:
rt_thread_detach(rt_thread_t thread)
线程启动函数:
rt_thread_startup(rt_thread_t thread)
获得当前线程:
rt_thread_self(void)
线程睡眠函数:
rt_err_t rt_thread_sleep(rt_tick_t tick)
rt_err_t rt_thread_delay(rt_tick_t tick) //tick决定睡眠时间
rt_thread_mdelay(rt_int32_t ms)
设置idle线程hook函数:
rt_thread_idle_sethook(void (*hook)(void))
删除idle线程hook函数:
rt_thread_idle_delhook(void (*hook)(void))
注意:空闲线程是一个线程状态永远为就绪态的线程,因此设置的钩子函数必须保证空闲线程在任何时刻都不会处于挂起状态,例如 rt_thread_delay(),rt_sem_take() 等可能会导致线程挂起的函数都不能使用。
设置线程调度器hook函数:
rt_scheduler_sethook(void (*hook)(struct rt_thread *from, struct rt_thread *to))
三、动态创建线程
在RT-Thread中,动态创建的线程是由内核堆分配的内存来存储线程的控制块和栈空间。当动态创建的线程完成其任务并被终止时,RT-Thread会释放该线程使用的内存空间。这是由RT-Thread内核的垃圾回收机制来完成的,它会将已经终止的线程的控制块和栈空间归还给内核堆。因此,当您使用RT-Thread动态创建线程时,无需担心内存释放的问题,RT-Thread内核会自动处理它。所以在动态创建线程的时候,线程删除函数可用可不用。
下面是动态创建一个led线程的代码,运行下面程序,系统将会创建一个led的线程,出现led闪烁的现象:
#include <rtthread.h>
#include "board.h"
rt_thread_t led1_th_ptr = NULL;
void LED1_th_entry(void *parameter) //LED1线程
{
uint8_t led1 = rt_pin_get("PF.9"); //获取led引脚号
rt_pin_mode(led1, PIN_MODE_OUTPUT); //设置io口模式
rt_kprintf("led1_th_ptr is running...\n");
rt_pin_write(led1, PIN_LOW); //IO给低电平
rt_thread_mdelay(500);
rt_pin_write(led1, PIN_HIGH);
rt_thread_mdelay(500);
}
int main(void)
{
while (1)
{
led1_th_ptr = rt_thread_create("led1_th",LED1_th_entry,NULL,1024,20,5); //动态创建LED1线程
if(led1_th_ptr == RT_NULL)
{
rt_kprintf("led1_th_ptr create error...\n");
return RT_ERROR; //创建失败返回错误码
}
else
{
rt_kprintf("led1_th_ptr create successed ...\n"); //创建成功后打印成功信息
if(rt_thread_startup(led1_th_ptr) == RT_EOK) //启动LED1线程
{
rt_kprintf("led1_th_ptr startup successed ...\n");//启动成功打印成功信息
}
else {
rt_kprintf("led1_th_ptr startup error ...\n"); //启动失败打印错误信息
return RT_ERROR; //返回一个错误码
}
}
rt_thread_mdelay(1000);
rt_kprintf("\n");
}
}
四、静态创建线程:
关于静态创建线程,RT-Thread 并不会自动释放线程所使用的内存。在静态创建线程时,需要手动分配内存资源,并传递给 rt_thread_init
函数。由于内存是静态分配的,RT-Thread 不会自动释放它。如果你想要释放线程所使用的内存,需要在线程结束时手动处理。但是,对于静态创建的线程,通常不需要释放内存,因为它们的生命周期通常与整个应用程序相同。在静态创建的线程运行完后,我们需要用线程脱离函数rt_thread_detach()将线程脱离,这样才能重新静态创建线程。
下面是静态创建一个led的线程代码:
#include <rtthread.h>
#include "board.h"
struct rt_thread led2_th_ptr;
void LED2_th_entry(void *parameter) //LED2线程
{
uint8_t led2 = rt_pin_get("PF.10"); //获取led引脚号
rt_pin_mode(led2, PIN_MODE_OUTPUT); //设置io口模式
rt_kprintf("led2_th_ptr is running...\n");
rt_pin_write(led2, PIN_HIGH); //IO给低电平
rt_thread_mdelay(500);
rt_pin_write(led2, PIN_LOW);
rt_thread_mdelay(500);
}
int main(void)
{
rt_uint8_t rt = 0;
rt_uint8_t th2_stack[1024]={0};
while (1)
{
rt = rt_thread_init(&led2_th_ptr, "led2_th", LED2_th_entry, NULL,
th2_stack,sizeof(th2_stack), 19, 5); //静态创建LED2线程
if(rt < 0)
{
rt_kprintf("led2_th_ptr create error...\n");
return rt;
}
else {
rt_kprintf("led2_th_ptr create successed ...\n");
if(rt_thread_startup(&led2_th_ptr) == RT_EOK) //启动LED2线程
{
rt_kprintf("led2_th_ptr startup successed ...\n");
}
}
rt_thread_mdelay(1000);
rt_thread_detach(&led2_th_ptr); //静态LED2线程脱离
rt_kprintf("\n");
}
}
五、动态和静态创建线程结合+线程调度hook函数使用
在整个系统的运行时,系统都处于线程运行、中断触发 - 响应中断、切换到其他线程,甚至是线程间的切换过程中,或者说系统的上下文切换是系统中最普遍的事件。有时用户可能会想知道在一个时刻发生了什么样的线程切换,可以通过调用线程调度hook函数rt_scheduler_sethook()接口设置一个相应的钩子函数。在系统线程切换时,这个钩子函数将被调用。
下面是动态创建led1线程和静态创建led2线程并使用线程调度hook函数查看线程调度情况的代码,运行这段代码,开发板上两个led灯交替闪烁,并且在串口中打印线程调度的关系:
#include <rtthread.h>
#include "board.h"
rt_thread_t led1_th_ptr = NULL;
struct rt_thread led2_th_ptr;
void LED1_th_entry(void *parameter) //LED1线程
{
uint8_t led1 = rt_pin_get("PF.9"); //获取led引脚号
rt_pin_mode(led1, PIN_MODE_OUTPUT); //设置io口模式
rt_kprintf("led1_th_ptr is running...\n");
rt_pin_write(led1, PIN_LOW); //IO给低电平
rt_thread_mdelay(500);
rt_pin_write(led1, PIN_HIGH);
rt_thread_mdelay(500);
}
void LED2_th_entry(void *parameter) //LED2线程
{
uint8_t led2 = rt_pin_get("PF.10"); //获取led引脚号
rt_pin_mode(led2, PIN_MODE_OUTPUT); //设置io口模式
rt_kprintf("led2_th_ptr is running...\n");
rt_pin_write(led2, PIN_HIGH); //IO给低电平
rt_thread_mdelay(500);
rt_pin_write(led2, PIN_LOW);
rt_thread_mdelay(500);
}
void scheduler_hook(struct rt_thread *from, struct rt_thread *to) //钩子函数
{
rt_kprintf("from:%s ---> to:%s\n",from->name,to->name); //打印线程切换关系
}
int main(void)
{
rt_uint8_t rt = 0;
rt_uint8_t th2_stack[1024]={0};
rt_scheduler_sethook(scheduler_hook); //设置调度器钩子函数
while (1)
{
rt = rt_thread_init(&led2_th_ptr, "led2_th", LED2_th_entry, NULL, th2_stack,sizeof(th2_stack), 19, 5); //静态创建LED2线程
led1_th_ptr = rt_thread_create("led1_th",LED1_th_entry,NULL,1024,20,5); //动态创建LED1线程
if(led1_th_ptr == RT_NULL)
{
rt_kprintf("led1_th_ptr create error...\n");
return RT_ERROR;
}
else
{
rt_kprintf("led1_th_ptr create successed ...\n");
if(rt_thread_startup(led1_th_ptr) == RT_EOK) //启动LED1线程
{
rt_kprintf("led1_th_ptr startup successed ...\n");
}
else {
rt_kprintf("led1_th_ptr startup error ...\n");
return RT_ERROR;
}
}
if(rt < 0)
{
rt_kprintf("led2_th_ptr create error...\n");
return rt;
}
else {
rt_kprintf("led2_th_ptr create successed ...\n");
if(rt_thread_startup(&led2_th_ptr) == RT_EOK) //启动LED2线程
{
rt_kprintf("led2_th_ptr startup successed ...\n");
}
}
rt_thread_mdelay(1000);
rt_thread_detach(&led2_th_ptr); //静态LED2线程脱离
rt_kprintf("\n");
}
}