线程的基本知识

今天给大家分享一点rt-thread的基础知识。
什么是线程?
人们在生活中处理复杂问题时,惯用的方法就是分而治之,即把一个大问题分解成多个相对简单、比较容易解决的小问题,小问题逐个被解决了,大问题也就随之解决了。同样,在设计一个较为复杂的应用程序时,也通常把一个大型任务分解成多个小任务,然后通过运行这些小任务,最终达到完成大任务的目的。
在裸机系统中, 系统的主体就是 main 函数里面顺序执行的无限循环,这个无限循环里面 cpu 按照顺序完成各种事情。在多线程系统中,我们根据功能的不同,把整个系统分割成一个个独立的且无法返回的函数,这个函数我们称为线程。
线程由哪些部分组成?
rt-thread 中的线程由三部分组成:线程代码(函数)、线程控制块、线程堆栈。
线程栈
在一个裸机系统中, 如果有全局变量,有子函数调用,有中断发生。那么系统在运行的时候,全局变量放在哪里,子函数调用时,局部变量放在哪里, 中断发生时,函数返回地址发哪里。
如果只是单纯的裸机编程,它们放哪里我们不用管,但是如果要写一个 rtos,这些种种环境参数,我们必须弄清楚他们是如何存储的。
在裸机系统中,他们统统放在一个叫栈的地方,栈是单片机 ram 里面一段连续的内存空间,栈的大小一般在启动文件或者链接脚本里面指定, 最后由 c 库函数_main 进行初始化。
但是, 在多线程系统中,每个线程都是独立的,互不干扰的,所以要为每个线程都分配独立的栈空间,这个栈空间通常是一个预先定义好的全局数组, 也可以是动态分配的一段内存空间,但它们都存在于 ram 中。如:
staticrt_uint8_tled_stack[512];
线程栈其实就是一个预先定义好的全局数据,数据类型为rt_uint8_t,大小我们设置为 512。在 rt-thread 中,凡是涉及到数据类型的地方, rtthread 都会将标准的 c 数据类型用 typedef 重新取一个类型名, 以“rt”前缀开头。这些经过重定义的数据类型放在 rtdef.h ,如:
线程控制块
在 rt-thread 中,线程控制块由结构体 struct rt_thread 表示,线程控制块是操作系统用于管理线程的一个数据结构,它会存放线程的一些信息,例如优先级、线程名称、线程状态等,也包含线程与线程之间连接用的链表结构,线程等待事件集合等,详细定义如下(在rtdef.h中定义):
为led线程定义一个线程控制块:
staticstructrt_threadled_thread;
线程函数
线程控制块中的 entry 是线程的入口函数,它是线程实现预期功能的函数。线程的入口函数由用户设
计实现,一般有以下两种代码形式:
无限循环模式:
在实时系统中,线程通常是被动式的:这个是由实时系统的特性所决定的,实时系统通常总是等待外界事件的发生,而后进行相应的服务:
顺序执行或有限次循环模式:
如简单的顺序语句、 do whlie() 或 for() 循环等,此类线程不会循环或不会永久循环,可谓是 “一次性”线程,一定会被执行完毕。在执行完毕后,线程将被系统自动删除。
动态线程与静态线程
我们的用户线程有两种创建方式,一种是静态线程,另一种是动态线程。
创建静态线程的函数:
返回值为错误代码。
创建动态线程的函数:
返回值为线程控制块 。
线程创建实例
创建一个静态线程
1、确定线程栈
2、定义线程控制块
3、创建线程函数。
#include #include #include /*静态线程相关宏定义*/ #definethread_priority25/*优先级*/ #definestack_size512/*栈大小*/ #definetimeslice5/*时间片*/ /*线程三要素*/ staticrt_uint8_tstatic_thread_stack[stack_size];/*线程栈*/ staticstructrt_threadstatic_thread;/*线程控制块*/ staticvoidstatic_thread_entry(void*parameter);/*线程入口函数*/ /*静态线程入口函数*/ staticvoidstatic_thread_entry(void*parameter) { rt_uint32_ti=0; rt_kprintf(thisisstaticthread!\n); /*无限循环*/ while(1) { rt_kprintf(staticthreadcount:%d\r\n,++i); /*等待0.5s,让出cpu权限,切换到其他线程*/ rt_thread_delay(500); } } /*主函数*/ intmain(void) { rt_err_tresult; /*创建静态线程:优先级 25 ,时间片 5个系统滴答,线程栈512字节*/ result=rt_thread_init(&static_thread, static_thread, static_thread_entry, rt_null, (rt_uint8_t*)&static_thread_stack[0], stack_size, thread_priority, timeslice); /*创建成功则启动静态线程*/ if(result==rt_eok) { rt_thread_startup(&static_thread); } }
运行结果为:
可见,在t-thread中创建一个线程需要线程栈、线程控制块与线程函数这三要素。除此之外,需要设置一个线程优先级,因为rt-thread的调度器是基于优先级的抢占式调度算法。还需要设置一个时间片参数,这个用于多个线程具有同等优先级的情况下,采用时间片的轮转调度算法进行调度,这个值与时间节拍有关,每一秒的节拍数可在rtconfig.h里进行设置:
在这里我们只创建一个线程,所以时间片我们没有用到,但也需要传递一个时间片的值给rt_thread_init函数。最后,在主函数里调用相关接口创建一个静态线程,创建成功则启动该线程。
创建一个动态线程
创建动态线程与创建静态线程类似:
#include #include #include /*动态线程相关宏定义*/ #definethread_priority25/*优先级*/ #definestack_size512/*栈大小*/ #definetimeslice5/*时间片*/ /*线程三要素*/ staticrt_uint8_tdynamic_thread_stack[stack_size];/*线程栈*/ staticstructrt_threaddynamic_thread;/*线程控制块*/ staticvoiddynamic_thread_entry(void*parameter);/*线程入口函数*/ /*动态线程入口函数*/ staticvoiddynamic_thread_entry(void*parameter) { rt_uint32_ti; /*无限循环*/ while(1) { for(i=0;i<5;i++) { rt_kprintf(dynamicthreadcount:%d\r\n,i); /*等待1s,让出cpu权限,切换到其他线程*/ rt_thread_delay(500); } } } /*主函数*/ intmain(void) { rt_thread_ttid;//动态线程句柄 /*创建动态线程:优先级 25 ,时间片 5个系统滴答,线程栈512字节*/ tid=rt_thread_create(dynamic_thread, dynamic_thread_entry, rt_null, stack_size, thread_priority, timeslice); /*创建成功则启动动态线程*/ if(tid!=rt_null) { rt_thread_startup(tid); } }
运行结果:
静态线程vs动态线程
上例中,从运行结果上看,是没有任何差别的!那么,我们在实际中如何抉择?
使用静态线程时,必须先定义静态的线程控制块,并且定义好栈空间,然后调用rt_thread_init()函数来完成线程的初始化工作。采用这种方式,线程控制块和堆栈占用的内存会放在 rw/zi 段,这段空间在编译时就已经确定,它不是可以动态分配的,所以不能被释放,而只能使用 rt_thread_detach()函数将该线程控制块从对象管理器中脱离。
使用动态定义方式 rt_thread_create()时, rt-thread 会动态申请线程控制块和堆栈空间。在编译时,编译器是不会感知到这段空间的,只有在程序运行时, rt-thread 才会从系统堆中申请分配这段内存空间,当不需要使用该线程时,调用 rt_thread_delete()函数就会将这段申请的内存空间重新释放到内存堆中。
这两种方式各有利弊,静态定义方式会占用 rw/zi 空间,但是不需要动态分配内存,运行时效率较高,实时性较好。动态方式不会占用额外的 rw/zi 空间,占用空间小,但是运行时需要动态分配内存,效率没有静态方式高。
总的来说,这两种方式就是空间和时间效率的平衡,可以根据实际环境需求选择采用具体的分配方式。就像c编程中,何时使用动态空间,何时使用静态空间,也需要根据实际情况平衡选择。

基于西门子400 PLC和ACS400变频器实现造纸机传动控制系统的设计
电磁压力传感器的分类
One Resistor Takes Heat from S
赛灵思世博会作品展
三星双屏显示专利曝光,或应用在Galaxy S11e设计上
线程的基本知识
CPU的6个主要寄存器
微软确认Windows 10 21H2&quot;太阳谷&quot;是大更新
朋友想做一款直播系统平台,我应该阻止他吗?
AI+大数据的组合,打造人与机器的全新世界
输入变频滤波器的结构及工作原理
石墨烯、特斯拉超级电池研究最新进展
德国政府大力推动电池德国造 未来4年内为电池研究工厂项目追加投资5亿欧元
IGBT深度报告:新能源发展的核心部件
低压低噪音精密放大器AD8655的性能特点及应用范围
【学习打卡】OpenHarmony开源项目介绍
什么是bonding?
浅谈Proteus模拟中的信号传输
中国工程院院士许祖彦:评测结果证明激光电视更加护眼
倍捷:48小时完成产品定制的“诀窍”