当前位置:   article > 正文

RT-Thread学习笔记一:线程基本了解_rt_thread_create优先级一致

rt_thread_create优先级一致

1.线程:

复杂任务分解为单个独立的可调度的单元,每个单元对应的程序实体为线程。

2.RT-Thread线程调度:

线程调度为抢占式,即从线程就绪列表中找出优先级最高的线程,然后运行。若线程优先级相同,则执行时间片轮转方式调度线程,单位为一个时钟节拍。

创建线程的时候,可以配置线程的时间片参数。时间片仅对优先级相同的就绪线程有效。

时间片的作用是约束线程单次运行的时长,其单位是系统时钟节拍(OS Tick)。

例如:A:10 B:5,则A执行10个节拍,B执行5个节拍。

除了中断处理函数、调度器上锁部分的代码和禁止中断的代码是不可抢占的之外,系统的其他部分都是可以抢占的,包括线程调度器自身。0 优先级代表最高优先级,最低优先级留给空闲线程使用;

3.线程状态分类:

初始状态———就绪状态——运行状态——挂起状态——关闭状态

初始状态,线程刚创建还未开始运行时处于的状态,此状态下,线程不参与调度。
就绪状态,线程具备运行条件的状态,等待被调度器调度执行。
运行状态,线程正在运行。
挂起状态,也称为阻塞状态。由于资源不可用或线程主动延时一段时间,而进入的状态。线程不能执行。
关闭状态,线程运行结束处于的状态。此时线程不参与调度。
 

涉及到的系统调用函数,在后面的学习会进行详细讲解。此处进行简单的说明:

rt_thread_create/init() 创建或初始化一个线程,此线程处于初始状态。

rt_thread_startup() 函数使得初始化状态的线程进入到就绪状态。

rt_thread_delay(),rt_sem_take(), rt_mutex_take() 等函数使得运行状态的线程进入到挂起状态。

rt_thread_resume(), rt_sem_release() 等函数使得挂起状态的线程返回到就绪状态。

rt_thread_delete/detach() 函数将挂起状态的线程更改为关闭状态。

rt_thread_exit(),处于运行状态的线程,运行结束,在线程的最后部分调用此函数,将状态更改为关闭状态。
 

空闲状态:优先级最低,永远为就绪态,不被挂起。 用处:回收被删除线程资源(回收僵尸线程)

rt_thread_yield():当前线程被换出,相同优先级的下一个就绪线程将被执行。

rt_schedule():当前线程并不一定被换出,而是在系统中选取就绪的优先级最高的线程执行。

4.线程的入口函数

入口函数是线程实现预期功能的函数。线程的入口函数由用户设计,一般有以下两种形式:

4.1 无限循环

即在while(1)函数中循环

  1. void thread_entry(void *parameter)
  2. {
  3. while(1)
  4. {
  5. /* 线程处理 */
  6. }
  7. }

4.2 有限循环

不会一直循环,执行完毕后被系统删除

5.线程创建:

RT-Thread 提供了先管理相关的系统函数:包含:创建 / 初始化线程、启动线程、运行线程、删除 / 脱离线程 等。

静态创建线程 动态创建线程两种方式

RT-Thread 内核采用面向对象的设计思想进行设计,系统级的基础设施都是一种内核对象,例如线程,信号量,互斥量,定时器等。

内核对象分为两类:静态内核对象和动态内核对象,静态内核对象通常放在RW 段和 ZI 段中,在系统启动后在程序中初始化;动态内核对象则是从内存堆中创建的,而后手工做初始化。
 

线程控制块
在 RT-Thread 中,线程控制块由结构体 struct rt_thread 表示。

线程控制块是操作系统用于管理线程的一个数据结构,它会存放线程的一些信息,例如优先级、线程名称、线程状态等。

线程控制块也包含线程与线程之间连接用的链表结构,线程等待事件集合等。

  1. struct rt_thread
  2. {
  3. /* rt 对象 */
  4. char name[RT_NAME_MAX]; /* 线程名称 */
  5. rt_uint8_t type; /* 对象类型 */
  6. rt_uint8_t flags; /* 标志位 */
  7. rt_list_t list; /* 对象列表 */
  8. rt_list_t tlist; /* 线程列表 */
  9. /* 栈指针与入口指针 */
  10. void *sp; /* 栈指针 */
  11. void *entry; /* 线程入口函数指针 */
  12. void *parameter; /* 参数 */
  13. void *stack_addr; /* 栈地址指针 */
  14. rt_uint32_t stack_size; /* 栈大小 */
  15. /* 错误代码 */
  16. rt_err_t error; /* 线程错误代码 */
  17. rt_uint8_t stat; /* 线程状态 */
  18. ....
  19. /* 优先级 */
  20. rt_uint8_t current_priority; /* 当前优先级 */
  21. rt_uint8_t init_priority; /* 初始优先级 */
  22. rt_uint32_t number_mask;
  23. ......
  24. rt_ubase_t init_tick; /* 线程初始化计数值 */
  25. rt_ubase_t remaining_tick; /* 线程剩余计数值 */
  26. struct rt_timer thread_timer; /* 内置线程定时器 */
  27. void (*cleanup)(struct rt_thread *tid); /* 线程退出清楚函数指针 */
  28. ...
  29. rt_uint32_t user_data; /* 用户数据 */
  30. };
  31. typedef struct rt_thread *rt_thread_t;

在 RT-Thread 中,要创建一个线程,并使得它能够被执行,需要两步:

  • 创建线程,此时一个新线程被创建,并处于初始状态。
  • 启动线程,此时线程由初始状态进入就绪状态,可以被调度器执行。

5.1 静态创建线程

不用动态分配内存,运行效率高,实时性好。用户分配栈空间和线程句柄。

内存不可释放,只能使用rt_thread_detach() 函数将该线程控制块脱离对象管理器。

  1. static rt_uint8_t thread1_stack[512]; //线程栈
  2. static struct rt_thread thread1; //线程控制块
  3. rt_thread_init(&thread1, //线程handle
  4. "thread1", //线程名称
  5. thread1_entry, //线程入口函数
  6. RT_NULL, //线程入口参数
  7. &thread1_stack[0], //线程栈地址
  8. sizeof(thread1_stack), //线程栈大小
  9. 15, //线程优先级
  10. 5); //线程时间片
  11. rt_thread_startup(&thread2); //线程进入就绪态

5.2 动态创建线程

参数简单,内存可以控制释放,用rt_thread_delete() 函数就会将这段申请的内存空间重新释放到内存堆中。

内存大小由系统分配,比静态效率低

  1. static rt_thread_t thread_id = RT_NULL;
  2. thread_id = rt_thread_create("dynamic_th", //名称
  3. dynamic_entry, //线程代码
  4. RT_NULL, //参数
  5. 1024, //栈大小
  6. 15, //优先级
  7. 20); //时间片
  8. if (thread_id != RT_NULL)
  9. rt_thread_startup(thread_id); //线程进入就绪态
  10. else
  11. rt_kprintf("dynamic_thread create failure\n");
  12. return RT_EOK;

线程创建成功,返回线程的控制块指针,也可称为线程句柄。

创建失败,则返回 RT_NULL。

5.3 启动线程

线程创建完成后,需要使其进入就绪状态,也就是启动线程。可以通过调用 rt_thread_startup() 函数来完成。其函数原型为:

  1. rt_err_t rt_thread_startup(rt_thread_t thread)


调用此函数成功后,会将线程放到相应优先级队列中等待调度。如果新启动的线程优先级比当前优先级高,将立即切换到这个线程。

参数 thread ,线程句柄,即线程控制块的指针。

线程启动成功,返回 RT_EOK;启动失败,则返回 -RT_ERROR。
 

6.示例

  1. #include <stdint.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <rtthread.h>
  5. #define THREAD_PRIORITY 25
  6. #define THREAD_STACK_SIZE 512
  7. #define THREAD_TIMESLICE 5
  8. void thread_entry(void *parameter)
  9. {
  10. rt_uint32_t count = 0;
  11. while(1)
  12. {
  13. /* 线程运行,打印计数 */
  14. rt_kprintf("thread run: %d\n", count ++);
  15. rt_thread_mdelay(500);
  16. }
  17. }
  18. int main(void)
  19. {
  20. rt_thread_t tid = RT_NULL;
  21. /* 创建线程, 名称是 thread_test, 入口是 thread_entry*/
  22. tid = rt_thread_create("thread_test",
  23. thread_entry, RT_NULL,
  24. THREAD_STACK_SIZE,
  25. THREAD_PRIORITY, THREAD_TIMESLICE);
  26. /* 线程创建成功,则启动线程 */
  27. if (tid != RT_NULL)
  28. {
  29. rt_thread_startup(tid);
  30. }
  31. return 0;
  32. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/67373
推荐阅读
相关标签
  

闽ICP备14008679号