RTthread学习笔记————第4章 线程管理

  • 什么是线程

线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。

RTthread学习笔记————第4章 线程管理_第1张图片

RT-Thread 的线程调度器是抢占式的,主要的工作就是从就绪线程列表中查找最高优先级任务,保证最高优先级的线程能够被运行,最高优先级的任务一旦就绪,总能得到 CPU 的控制权。

  • 线程有哪些状态

/* 线程控制块 */
struct rt_thread
{
    /* rt 对象 */
    char name[RT_NAME_MAX]; /* 线程名称 */
    rt_uint8_t type; /* 对象类型 */
    rt_uint8_t flags; /* 标志位 */
    rt_list_t list; /* 对象列表 */
    rt_list_t tlist; /* 线程列表 */
    /* 栈指针与入口指针*/
    void *sp; /* 栈指针 */
    void *entry; /* 入口函数指针 */
    void *parameter; /* 参数 */
    void *stack_addr; /* 栈地址指针 */
    rt_uint32_t stack_size; /* 栈大小 */
    /* 错误代码 */
    rt_err_t error; /* 线程错误代码 */
    rt_uint8_t stat; /* 线程状态 */
    /* 优先级 */
    rt_uint8_t current_priority; /* 当前优先级 */
    rt_uint8_t init_priority; /* 初始优先级 */
    rt_uint32_t number_mask;
    ......
    rt_ubase_t init_tick; /* 线程初始化计数值 */
    rt_ubase_t remaining_tick; /* 线程剩余计数值*/
    struct rt_timer thread_timer; /*内置线程定时器*/
    void (*cleanup)(struct rt_thread *tid); /* 线程退出清除函数 */
    rt_uint32_t user_data; /* 用户数据 */
};
/*其中 init_priority 是线程创建时指定的线程优先级,在线程运行过程当中是不会被改变的(除
非用户执行线程控制函数进行手动调整线程优先级)。cleanup 会在线程退出时,被空闲线程回调
一次以执行用户设置的清理现场等工作。最后的一个成员 user_data 可由用户挂接一些数据信息到
线程控制块中,以提供类似线程私有数据的实现。*/

RTthread学习笔记————第4章 线程管理_第2张图片

  1. 初始状态:线程刚开始创建还没开始运行时就处于初始状态;在初始状态下,线程不参与调度。此状态在 RT-Thread                   中的宏定义为 RT_THREAD_INIT。
  2. 就绪状态:在就绪状态下,线程按照优先级排队,等待被执行;一旦当前线程运行完毕让出处理器,操作系统会马上                   寻找最高优先级的就绪态线程运行。此状态在 RT-Thread 中的宏定义为RT_THREAD_READY。
  3. 运行状态:线程当前正在运行。在单核系统中,只有 rt_thread_self() 函数返回的线程处于运行状态;在多核系统                      中,可能就不止这一个线程处于运行状态。此状态在 RT-Thread 中的宏定义为RT_THREAD_RUNNING。
  4. 挂起状态:也称阻塞态。它可能因为资源不可用而挂起等待,或线程主动延时一段时间而挂起。在挂起状态下,线程                   不参与调度。此状态在 RT-Thread 中的宏定义为 RT_THREAD_SUSPEND
  5. 关闭状态:当线程运行结束时将处于关闭状态。关闭状态的线程不参与线程的调度。此状态在 RT-Thread中的宏定义                   为 RT_THREAD_CLOSE

RTthread学习笔记————第4章 线程管理_第3张图片

  • 什么是时间片

每个线程都有时间片这个参数,但时间片仅对优先级相同的就绪态线程有效。系统对优先级相同的就绪态线程采用时间片轮转的调度方式进行调度时,时间片起到约束线程单次运行时长的作用,其单位是一个系统节拍(OS Tick),假设有 2 个优先级相同的就绪态线程 A 与B,A 线程的时间片设置为 10,B 线程的时间片设置为 5,那么当系统中不存在比 A 优先级高的就绪态线程时,系统会在 A、B 线程间来回切换执行,并且每次对 A 线程执行 10 个节拍的时长,对B 线程执行 5 个节拍的时长。

  • 如何创建一个进程

RTthread学习笔记————第4章 线程管理_第4张图片

创建一个动态线程: (动态)

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);

RTthread学习笔记————第4章 线程管理_第5张图片

完全删除线程:(动态)

rt_err_t rt_thread_delete(rt_thread_t thread);

RTthread学习笔记————第4章 线程管理_第6张图片

 

初始化线程:(静态)

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);

RTthread学习笔记————第4章 线程管理_第7张图片

脱离线程:(静态)

rt_err_t rt_thread_detach (rt_thread_t thread);

RTthread学习笔记————第4章 线程管理_第8张图片

 启动线程

rt_err_t rt_thread_startup(rt_thread_t thread);

RTthread学习笔记————第4章 线程管理_第9张图片

获得当前线程
 

rt_thread_t rt_thread_self(void);

RTthread学习笔记————第4章 线程管理_第10张图片

使线程让出处理器资源

rt_err_t rt_thread_yield(void);

/*调用该函数后,当前线程首先把自己从它所在的就绪优先级线程队列中删除,然后把自己挂到
这个优先级队列链表的尾部,然后激活调度器进行线程上下文切换(如果当前优先级只有这一个线
程,则这个线程继续执行,不进行上下文切换动作)。*/

使线程睡眠

rt_err_t rt_thread_sleep(rt_tick_t tick);
rt_err_t rt_thread_delay(rt_tick_t tick);
rt_err_t rt_thread_mdelay(rt_int32_t ms);

RTthread学习笔记————第4章 线程管理_第11张图片

挂起和恢复线程

/*线程挂起使用下面的函数接口:*/
rt_err_t rt_thread_suspend (rt_thread_t thread);

RTthread学习笔记————第4章 线程管理_第12张图片

/*线程恢复使用下面的函数接口:*/
rt_err_t rt_thread_resume (rt_thread_t thread);

RTthread学习笔记————第4章 线程管理_第13张图片

控制线程

/*当需要对线程进行一些其他控制时,例如动态更改线程的优先级,可以调用如下函数接口:*/
rt_err_t rt_thread_control(rt_thread_t thread, rt_uint8_t cmd, void* arg);

RTthread学习笔记————第4章 线程管理_第14张图片

  • 为什么会出现空闲线程

设置空闲钩子

/*设置空闲钩子函数*/
rt_err_t rt_thread_idle_sethook(void (*hook)(void));

/*注意:空闲线程是一个线程状态永远为就绪态的线程,因此设置的钩子函数必须保证空闲线程在任
何时刻都不会处于挂起状态,例如 rt_thread_delay() , rt_sem_take() 等可能会导致线程挂起的函
数都不能使用。*/

RTthread学习笔记————第4章 线程管理_第15张图片

设置调度器钩子

/*请仔细编写你的钩子函数,稍有不慎将很可能导致整个系统运行不正常(在这个钩子函数中,
基本上不允许调用系统 API,更不应该导致当前运行的上下文挂起*/

void rt_scheduler_sethook(void (*hook)(struct rt_thread* from, struct
                                rt_thread* to));


/*钩子函数 hook()的声明如下:*/

void hook(struct rt_thread* from, struct rt_thread* to);

RTthread学习笔记————第4章 线程管理_第16张图片

  • RTthread小结

  1. 普通线程不能陷入死循环操作,必须要有让出 CPU 使用权的动作。
  2. 线程调度是基于优先级抢占的方式进行,优先级相同的线程通过时间片轮询执行。
  3. 动态线程的创建与删除调用接口 rt_thread_create()与 rt_thread_delete();静态线程的初始化与脱离调用接口rt_thread_init()与 rt_thread_detach()。要注意的是,由于能执行完毕的线程系统会自    动将其删除,所以不推荐用户使用删除/脱离线程接口。

 

注:文章参考培训教程 

你可能感兴趣的:(嵌入式,STM32,RTthread)