0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

线程的基本知识

黄工的嵌入式技术圈 来源:黄工的嵌入式技术圈 作者:黄工的嵌入式技术 2020-02-04 15:42 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

今天给大家分享一点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("dynamic thread count:%d \r\n", i);             /* 等待1s,让出cpu权限,切换到其他线程 */             rt_thread_delay(500);         }     } } /* 主函数 */ int main(void) {     rt_thread_t tid;  // 动态线程句柄     /* 创建动态线程 : 优先级 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编程中,何时使用动态空间,何时使用静态空间,也需要根据实际情况平衡选择。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 多线程
    +关注

    关注

    0

    文章

    279

    浏览量

    20911
  • 线程
    +关注

    关注

    0

    文章

    508

    浏览量

    20753
  • RT-Thread
    +关注

    关注

    32

    文章

    1540

    浏览量

    44250
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    Linux多线程对比单线程的优势

    在Linux系统中,线程是操作系统能够进行运算调度的最小单位。线程被包含在进程之中,是进程中的实际运行单位。一个进程可以拥有多个线程,这些线程共享相同的内存空间和系统资源。
    发表于 12-01 06:11

    舵机的基本知识

    舵机是一种能精确控制旋转角度的驱动装置,核心作用是将电信号转化为特定角度的机械运动,广泛用于需要精准定位的场景。 一、舵机的核心构成 舵机主要由四个关键部分组成,各部分协同工作实现角度控制。 直流电机 :提供基础动力,是舵机运动的动力源。 减速齿轮组 :降低电机转速、提升扭矩,让输出轴能带动更重的负载。 电位器(位置传感器) :实时检测输出轴的当前角度,将角度信息反馈给控制电路。 控制电路 :接收外部控制信号,对
    的头像 发表于 10-28 09:57 129次阅读

    国家信息中心与摩尔线程达成战略合作

    10月21日上午,国家信息中心与摩尔线程在北京举行战略合作协议签约仪式。国家信息中心主任徐强,摩尔线程创始人、董事长兼首席执行官张建中出席签约仪式。国家信息中心副主任周民与摩尔线程联合创始人兼首席运营官周苑代表双方签署战略合作协
    的头像 发表于 10-23 15:52 346次阅读

    rtt中建两个线程a和b,怎么确保线程a执行完立刻切到线程b?

    怎么获取从线程开始切换到切换完成用的总的CPU时钟节拍数量?
    发表于 10-10 06:37

    tcpip线程被mu0锁住导致网络线程无法使用怎么解决?

    各位好,我使用rtthread开发STM32F407VGT6芯片,程序有多个线程,每个线程都会创建一个socket,建立tcp连接或者udp连接,现在出现一个问题,程序长时间运行有概率死机,但是没有
    发表于 09-29 06:41

    低优先级线程无法调度怎么解决?

    1,设置了3,5,6,8几个优先级,设备在现场正常运行了一年多后,显示、前端、后端这3个低优先级线程异常了,表现为屏幕不动,前端采集数据没有变化等,其他高优先级的线程如通讯,按键都能正常运行,通讯有喂狗操作,停止通讯,会看门狗复位,有没有朋友帮忙提示一下,谢谢各位!
    发表于 09-25 07:33

    线程删除时遇到断言,是什么原因导致的?

    在一个线程中调用线程删除函数删除另外一个线程,这2个线程的优先级是相等的,被删除的线程也是动态创建的,出现了下面的断言内容,一般是什么情况导
    发表于 09-12 06:08

    rtth studio中nano 如何创建动态线程

    有没有大佬,可以说一下为什么静态线程可以正常使用,动态线程怎么也使用不了。 具体需要什么配置才能使用动态线程创建。谢谢!
    发表于 09-11 06:01

    A25:MCU系统器件知识与应用专题--MCU、EEPROM/FLASH和晶体/晶振知识及应用案例

    、晶振等),分别介绍器件的特点、用途、关键选型参数、供应资源,给出具体的选型应用案例。 主要内容: 1、从人体模型、BMS单板架构,看MCU系统的位置及作用; 2、介绍MCU的基本知识(物料
    的头像 发表于 09-09 10:24 426次阅读
    A25:MCU系统器件<b class='flag-5'>知识</b>与应用专题--MCU、EEPROM/FLASH和晶体/晶振<b class='flag-5'>知识</b>及应用案例

    rtt studio中nano 如何创建动态线程

    有没有大佬,可以说一下为什么静态线程可以正常使用,动态线程怎么也使用不了。 具体需要什么配置才能使用动态线程创建。谢谢!
    发表于 08-22 06:19

    UVC+MSC实现中MSC线程未运行的原因?

    我正在尝试使用 EZUSB 运行 UVC + MSC。我有以下内容。但看起来只有 UVC 线程在运行,而 MSC 没有运行。fw 不响应 MSC 命令。我确保 LPM 已被禁用,只是为了检查传感器
    发表于 07-16 07:08

    深度剖析 RT-Thread 线程调度流程

    RT-Thread调度第一个线程的主要流程分如下:rtthread_startup:RTT的启动函数,主要负责板级驱动,调度器,系统线程初始化,启动调度的工作
    的头像 发表于 06-25 18:24 1434次阅读
    深度剖析 RT-Thread <b class='flag-5'>线程</b>调度流程

    线程的安全注意事项

    线程安全是指多个线程同时访问或修改共享资源时,能够保证程序的正确性和可靠性。 开发者选择TaskPool或Worker进行多线程开发时,在TaskPool和Worker的工作线程中导
    发表于 06-20 07:49

    鸿蒙5开发宝藏案例分享---跨线程性能优化指南

    发现鸿蒙宝藏:跨线程序列化性能优化实战指南 大家好呀!今天在翻鸿蒙文档时挖到一个超级实用的工具—— DevEco Profiler的序列化检测功能 !平时用<span class
    发表于 06-12 17:13

    直流电机基本知识彩色PDF来啦

    1 直流电机的工作原理、主要结构、额定值 2直流电机的电枢绕组 3直流电机的电枢反应 4电枢绕组感应电动势和电磁转矩 5直流电机换向 一文带你了解直流电机基本知识,免费下载
    发表于 02-28 01:28