介绍
在FreeRTOS、UCOS操作系统里面叫任务,在RT-Thread里面叫线程,其实都是一个意思,都是假想这个线程(任务)独占CPU。而在RT-Thread的线程里面,除了有像FreeRTOS、UCOS那种静态创建任务,还有可以动态创建,动态创建就提供了很大便利。
线程调度
RT-Thread中提供的线程调度器是基于优先级的全抢占式调度:在系统中除了中断处理函数、调度器上锁部分的代码和禁止中断的代码是不可抢占的之外,系统的其他部分都是可以抢占的,包括线程调度器自身。优先级从0~255(也可以配置成8个或者32个优先级),值越小优先级越高,最小的优先级分配给空闲线程。系统总是运行着就绪的最高优先级的任务,即使正在运行着低优先级也会抢占过去。同等优先级的,就按照原先设定的时间进行轮询式运行。
线程状态
因为线程是假象自己拥有着CPU使用权,但是同一时刻内只有一个线程正在运行,那么不在运行的线程,就会处于其他状态。
状态 | 描述 |
---|---|
RT_THREAD_INIT | 线程初始状态。当线程刚开始创建还没开始运行时就处于这个状态;在这个状态下,线程不参与调度 |
RT_THREAD_SUSPEND | 挂起态、阻塞态。线程此时被挂起:它可能因为资源不可用而挂起等待;或线程主动延时一段时间而被挂起。在这个状态下,线程不参与调度 |
RT_THREAD_READY | 就绪态。线程正在运行;或当前线程运行完让出处理器后,操作系统寻找最高优先级的就绪态线程运行 |
RT_THREAD_RUNNING | 运行态。线程当前正在运行,在单核系统中,只有rt_thread_self()函数返回的线程处于这个状态;在多核系统中则不受这个限制。 |
RT_THREAD_CLOSE | 线程结束态。当线程运行结束时将处于这个状态。这个状态的线程不参与线程的调度。 |
RT_THREAD_INIT | 线程初始状态。当线程刚开始创建还没开始运行时就处于这个状态;在这个状态下,线程不参与调度 |
线程状态转换如图
线程创建和删除
在main.c文件目录下创建一个单独的文件(不影响其他文件),这个文件用于创建启动之后关于内核相关资源的测试。
函数原型
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_err_t rt_thread_delete(rt_thread_t thread)
启动测试
#include <rtthread.h>
void create_th(void *para)
{
rt_kprintf("create entry!\r\n");
}
int init_func(void)
{
rt_thread_t t_thread;
t_thread = rt_thread_create(
"create", // name the name of thread, which shall be unique
create_th, // entry the entry function of thread,
NULL, // parameter the parameter of thread enter function
1024, // stack_size the size of thread stack
12, // priority the priority of thread
5 // tick the time slice if there are same priority thread
);
rt_thread_startup(t_thread);
return RT_EOK;
}
INIT_APP_EXPORT(init_func);
这里init_func函数创建了一个线程,并且INIT_APP_EXPORT宏将这个函数在内核初始化时候就执行这个函数(这一点,在使用上带来了一定的便利),线程创建好并不会立刻运行,而是会进入初始化好的状态,需要调用rt_thread_startup函数来启动线程,启动之后才会从线程入口开始执行,如果没有假如循环,线程函数执行完就会自动删除了。
创建一个线程,并立刻启动
rt_thread_startup(rt_thread_create("thread1", thread_entry, NULL, 256, 25, 5));
在线程里面,运行四次后删除
void thread_entry(void* parameter)
{
rt_uint32_t count = 0;
while (1)
{
/* 打印线程计数值输出 */
rt_kprintf("thread count: %d\n", count++);
/* 运行4次后删除 */
if (count > 4)
{
rt_thread_delete(rt_thread_self());
}
rt_thread_mdelay(1000);
}
}
但是这种情况会出现一个错误
(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer) assertion failed at function:rt_timer_control, line number:463
在线程删除的时候就抛出了这个错误,原因是线程里面不能使用rt_thread_delete函数来删除自身,如果想删除自身,就让程序跳出死循环走出线程入口函数,系统就会自动回收这个线程了。
针对rt_thread_delete函数,一般只能由其他线程调用或在定时器超时函数中调用。
2.1.4 线程初始化和脱离
线程的初始化和创建线程类似,功能都是生成可运行的线程,但是初始话与创建不太一样,顾名思义,“创建”是由无到有,“初始化”是将有的变成初始化样子。创建线程就是动态的分配内存去维护线程,而初始化线程就不太一样,除了要事先静态定义好线程(并不是句柄,注意区分),还要分配好线程运行时所需要的栈空间。
线程脱离也是和删除线程类似,是针对由通过初始化的线程进行从内核中脱离。
函数原型
rt_err_t 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_err_t rt_thread_detach(rt_thread_t thread)
线程控制块相关定义
#define THREAD_STACK_SIZE 256
#define THREAD_PRIORITY 25
#define THREAD_TIMESLICE 5
初始化
/* 初始化线程2 */
result = rt_thread_init(
&thread2, "thread2", /* 线程名:t2 */
thread_entry, (void*)2, /* 线程的入口是thread_entry,入口参数是2 */
&thread2_stack[0], sizeof(thread2_stack), /* 线程栈是thread2_stack */
THREAD_PRIORITY + 1, 10);
if (result == RT_EOK) /* 如果返回正确,启动线程2 */
{
rt_thread_startup(&thread2);
}
脱离
rt_thread_detach(&thread2);
线程挂起和恢复
可以主动使用“rt_thread_suspend”函数进行挂起,但要立刻使用“rt_schedule”函数来切换一次。主动挂起后,认为线程在某一时刻不再执行,直到恢复。
函数原型
rt_err_t rt_thread_suspend(rt_thread_t thread)
rt_err_t rt_thread_resume(rt_thread_t thread)
创建一个新线程,挂起后运行4s重新恢复。
/* 创建线程3 */
thread3 = rt_thread_create("thread3", thread_entry, (void *)3, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE);
if (thread3 != RT_NULL)
{
rt_thread_startup(thread3);
}
rt_thread_suspend(thread3);
rt_thread_mdelay(4000);
rt_thread_resume(thread3);
结尾
rtthread学习中,打开主页查看更多笔记