RT-Thread: 线程创建及应用

news/2024/7/3 2:15:10

说明:这里记录 RT-Thread 的具体创建过程,相当于线程创建的一个模式,具体线程相关的信息没做介绍, RT-Thread 的官方文档里面已经有详细介绍,如有需要请移步官网。

官网链接:https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/thread/thread

1. 线程相关函数:

//创建动态线程
//一个线程要成为可执行的对象,就必须由操作系统的内核来为它创建一个线程。可以通过如下的接口创建一个动态线程:
//调用这个函数时,系统会从动态堆内存中分配一个线程句柄以及按照参数中指定的栈大小从动态堆内存中分配相应的空间。
//分配出来的栈空间是按照 rtconfig.h 中配置的 RT_ALIGN_SIZE 方式对齐。线程创建 rt_thread_create() 
//的参数和返回值见下表:	
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_create() 创建出来的线程,当不需要使用,或者运行出错时,
//我们可以使用下面的函数接口来从系统中把线程完全删除掉:
rt_err_t rt_thread_delete(rt_thread_t thread);

							
//初始化静态线程
//线程的初始化可以使用下面的函数接口完成,来初始化静态线程对象:
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_thread_init() 初始化的线程,使用 rt_thread_detach() 
//将使线程对象在线程队列和内核对象管理器中被脱离。线程脱离函数如下:
rt_err_t rt_thread_detach (rt_thread_t thread);


//启动线程
//创建(初始化)的线程状态处于初始状态,并未进入就绪线程的调度队列,
//我们可以在线程初始化 / 创建成功后调用下面的函数接口让该线程进入就绪态:
rt_err_t rt_thread_startup(rt_thread_t thread);


//获得当前线程
//在程序的运行过程中,相同的一段代码可能会被多个线程执行,
//在执行的时候可以通过下面的函数接口获得当前执行的线程句柄:
rt_thread_t rt_thread_self(void);


//使线程让出处理器资源
//当前线程的时间片用完或者该线程主动要求让出处理器资源时,它将不再占有处理器,
//调度器会选择相同优先级的下一个线程执行。线程调用这个接口后,这个线程仍然在就绪队列中。
//线程让出处理器使用下面的函数接口:

rt_err_t rt_thread_yield(void);


//使线程睡眠
//在实际应用中,我们有时需要让运行的当前线程延迟一段时间,在指定的时间到达后重新运行,
//这就叫做 “线程睡眠”。线程睡眠可使用以下三个函数接口:
//这三个函数接口的作用相同,调用它们可以使当前线程挂起一段指定的时间,当这个时间过后,
//线程会被唤醒并再次进入就绪状态。这个函数接受一个参数,该参数指定了线程的休眠时间。
//线程睡眠接口 rt_thread_sleep/delay/mdelay() 的参数和返回值见下表:
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);


//挂起和恢复线程
//当线程调用 rt_thread_delay() 时,线程将主动挂起;当调用 rt_sem_take(),rt_mb_recv() 
//等函数时,资源不可使用也将导致线程挂起。处于挂起状态的线程,如果其等待的资源超时(超过其设定的等待时间),
//那么该线程将不再等待这些资源,并返回到就绪状态;或者,当其他线程释放掉该线程所等待的资源时,该线程也会返回到就绪状态。
rt_err_t rt_thread_suspend (rt_thread_t thread);


//恢复挂起线程
//恢复线程就是让挂起的线程重新进入就绪状态,并将线程放入系统的就绪队列中;如果被恢复线程在所有就绪态线程中,
//位于最高优先级链表的第一位,那么系统将进行线程上下文的切换。线程恢复使用下面的函数接口:
rt_err_t rt_thread_resume (rt_thread_t thread);


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


//设置和删除空闲钩子
//空闲钩子函数是空闲线程的钩子函数,如果设置了空闲钩子函数,就可以在系统执行空闲线程时,
//自动执行空闲钩子函数来做一些其他事情,比如系统指示灯。设置 / 删除空闲钩子的接口如下:
rt_err_t rt_thread_idle_sethook(void (*hook)(void));//设置空闲钩子函数
rt_err_t rt_thread_idle_delhook(void (*hook)(void));//删除空闲钩子函数


//设置调度器钩子
//在整个系统的运行时,系统都处于线程运行、中断触发 - 响应中断、切换到其他线程,甚至是线程间的切换过程中,
//或者说系统的上下文切换是系统中最普遍的事件。有时用户可能会想知道在一个时刻发生了什么样的线程切换,
//可以通过调用下面的函数接口设置一个相应的钩子函数。在系统线程切换时,这个钩子函数将被调用:
//注:请仔细编写你的钩子函数,稍有不慎将很可能导致整个系统运行不正常(在这个钩子函数中,基本上不允许调用系统
//API,更不应该导致当前运行的上下文挂起)。
void rt_scheduler_sethook(void (*hook)(struct rt_thread* from, struct rt_thread* to));

2. 示例1,提取实际项目有关线程创建和线程入口函数部分代码,以作备忘。

/* 线程入口函数     打印输出 ADC 处理完一轮的数据 */
static void adc_buffer_printf_entry(void *param)
{
    while(1)
    {
        /* 一直等,直到获取到信号量 */
        rt_sem_take(&rx_sem_adc_dma, RT_WAITING_FOREVER); /*  等待时间:一直等 */

        for (uint8_t var = 0; var < 60; ++var)
        {
            rt_kprintf("VH12 CH = %d, DATA = %d \r\n", var,adc_data.adc_all_VH12[var]);
        }

        for (uint8_t var = 0; var < 10; ++var)
        {
            rt_kprintf("NTC CH = %d, DATA = %d \r\n", var,adc_data.adc_all_NTC[var]);
        }

        rt_kprintf("VIN_V =  %d \r\n",adc_data.adc_all_V_I[0]);
        rt_kprintf("VIN_I =  %d \r\n",adc_data.adc_all_V_I[1]);
        rt_kprintf("VHT_V =  %d \r\n",adc_data.adc_all_V_I[2]);
        rt_kprintf("VHT_V =  %d \r\n",adc_data.adc_all_V_I[3]);

        rt_thread_yield();/* 放弃剩余时间片,进行一次线程切换 */

    }
}

/* 创建线程 函数 */
void adc_buffer_printf(void)
{
    rt_thread_t tid1;//创建线程控制块指针来接收线程创建函数的返回值,目的是通过返回值判断线程是否创建ok

    /* 创建线程 */
    tid1 = rt_thread_create("adc_buffer_printf",     //线程名称,系统打印线程时会显示这个线程的名字
                            adc_buffer_printf_entry, //线程入口函数,入口函数函数名
                            RT_NULL,                 //入口参数
                            300,                     //线程栈大小,单位是字节
                            10,                      //设置优先级
                            100);                    //时间片参数,时间片是在有多个相同优先级线程时,这个线程每次被执行多少个时间片

    /* 如果获得线程控制块,启动这个线程 */
    if (tid1 != RT_NULL)
        rt_thread_startup(tid1); //启动线程
    else
       {
            rt_kprintf("Failed to create < adc_buffer_printf > \r\n"); //创建失败,打印输出,创建失败的具体 线程名
            return;
       }

}

3. 线程应用示例 2

下面给出在 Keil 模拟器环境下的应用示例。

/* 线程示例 */

#include <rtthread.h>

#define THREAD_PRIORITY         25
#define THREAD_STACK_SIZE       512
#define THREAD_TIMESLICE        5

static rt_thread_t tid1 = RT_NULL;

/* 线程 1 的入口函数 */
static void thread1_entry(void *parameter)
{
    rt_uint32_t count = 0;

    while (1)
    {
        /* 线程 1 采用低优先级运行,一直打印计数值 */
        rt_kprintf("thread1 count: %d\n", count ++);
        rt_thread_mdelay(500);
    }
}

ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;
/* 线程 2 入口 */
static void thread2_entry(void *param)
{
    rt_uint32_t count = 0;

    /* 线程 2 拥有较高的优先级,以抢占线程 1 而获得执行 */
    for (count = 0; count < 10 ; count++)
    {
        /* 线程 2 打印计数值 */
        rt_kprintf("thread2 count: %d\n", count);
    }
    rt_kprintf("thread2 exit\n");
    /* 线程 2 运行结束后也将自动被系统脱离 */
}

/* 线程示例 */
int thread_sample(void)
{
    /* 创建线程 1,名称是 thread1,入口是 thread1_entry*/
    tid1 = rt_thread_create("thread1",
                            thread1_entry, RT_NULL,
                            THREAD_STACK_SIZE,
                            THREAD_PRIORITY, THREAD_TIMESLICE);

    /* 如果获得线程控制块,启动这个线程 */
    if (tid1 != RT_NULL)
        rt_thread_startup(tid1);

    /* 初始化线程 2,名称是 thread2,入口是 thread2_entry */
    rt_thread_init(&thread2,
                   "thread2",
                   thread2_entry,
                   RT_NULL,
                   &thread2_stack[0],
                   sizeof(thread2_stack),
                   THREAD_PRIORITY - 1, THREAD_TIMESLICE);
    rt_thread_startup(&thread2);

    return 0;
}

/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(thread_sample, thread sample);


http://lihuaxi.xjx100.cn/news/1952276.html

相关文章

鸿蒙HarmonyOS学习手册_入门篇

鸿蒙HarmonyOS学习手册_入门篇 文章目录 鸿蒙HarmonyOS学习手册_入门篇入门快速入门开发准备基本概念UI框架应用模型工具准备 构建第一个ArkTS应用&#xff08;Stage模型&#xff09;-快速入门-入门创建ArkTS工程ArkTS工程目录结构&#xff08;Stage模型&#xff09;构建第一个…

【昕宝爸爸系列】如何将集合变成线程安全的?

如何将集合变成线程安全的? ✅典型解析&#x1f7e2;拓展知识仓☑️Java中都有哪些线程安全的集合&#xff1f;&#x1f7e0;线程安全集合类的优缺点是什么&#x1f7e1;如何选择合适的线程安全集合类☑️如何解决线程安全集合类并发冲突问题✔️乐观锁实现方式 (具体步骤)。✅…

ROS2 Humble学习笔记

本文发表与个人的github pages。部分内容未同步到这里。 想查看完整内容&#xff0c;请移步到ROS2 Humble学习笔记。 一、前言 2013年的时候已经接触ROS了&#xff0c;当时断断续续学习了一些ROS的基础知识。16年搬到深圳之后&#xff0c;也有幸参加过星火的一次关于ROS的一些…

怎么给IP证书更换IP地址

IP证书是由CA认证机构颁发的一种数字证书&#xff0c;可以为只有公网IP地址的网站提供数据加密服务。事实上&#xff0c;IP证书不仅可以提供加密传输服务&#xff0c;还可以验证网站的身份&#xff0c;保证数据传输的安全性。相对于传统基于域名的SSL证书&#xff0c;IP证书无需…

zippo打火机激光打标机

激光打标技术是一种高精度的加工方式&#xff0c;能够在各种材料表面进行精细的打标&#xff0c;包括金属、塑料、玻璃等。随着科技的不断进步&#xff0c;激光打标技术的应用范围越来越广泛&#xff0c;特别是在制造行业&#xff0c;已经成为一种重要的加工手段。 Zippo打火机…

【Wordpress高级教程】 Wordpress免插件建立站群,wordpress整站迁移/安装

提示&#xff1a;该方法适用于Wordpress的站点&#xff0c;且无需插件哦&#xff08;插件一般都需要付费的&#xff0c;博主比较穷&#xff0c;我们就通过技术来解决&#xff09; 文章目录 前言一、准备工作二、搭建站群1.打包wp-content2.导入新站点3.导出数据库4.修改数据库配…

GBASE南大通用分析型MPP数据库GBase8a的安全特性(2)

GBase南大通用自主研发的 GBase 8a MPP Cluster 产品&#xff08;简称GBase8a)是大数据时代成熟的分析型MPP数据库&#xff0c;具有多样化的平台选择、与时俱进的逻辑架构、海量数据高效存储、海量数据高速加载、海量数据高性能分析、弹性服务器资源伸缩、完善的系统资源管理、…

Kubernetes 调度器及其优化

一、 Kubernetes 调度器 ​在 Kubernetes 中&#xff0c;调度 是指将 Pod 放置到合适的节点上&#xff0c;以便对应节点上的 Kubelet 能够运行这些 Pod。 ​ 1、调度概览 调度器通过 Kubernetes 的监测&#xff08;Watch&#xff09;机制来发现集群中新创建且尚未被调度到节…