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

    文章

    278

    浏览量

    19946
  • 线程
    +关注

    关注

    0

    文章

    504

    浏览量

    19676
  • RT-Thread
    +关注

    关注

    31

    文章

    1285

    浏览量

    40094
收藏 人收藏

    评论

    相关推荐

    socket 多线程编程实现方法

    在现代网络编程中,多线程威廉希尔官方网站 被广泛应用于提高服务器的并发处理能力。Socket编程是网络通信的基础,而将多线程威廉希尔官方网站 应用于Socket编程,可以显著提升服务器的性能。 多线程编程的基本概念 多线
    的头像 发表于 11-12 14:16 340次阅读

    IGB基本知识汇总

    本文对IGBT领域的深入研究与综合剖析成果,首先与您分享关于IGBT的基本原理及其构成元素,如芯片、单管及模块等方面的知识。接下来将共同关注的是IGBT在各个行业中的广泛应用。
    的头像 发表于 10-29 10:15 393次阅读
    IGB<b class='flag-5'>基本知识</b>汇总

    CPU线程和程序线程的区别

    CPU的线程与程序的线程在概念、作用、实现方式以及性能影响等方面存在显著差异。以下是对两者区别的详细阐述,旨在深入探讨这一威廉希尔官方网站 话题。
    的头像 发表于 09-02 11:18 981次阅读

    堆栈和内存的基本知识

    本文主要聊聊关于堆栈的内容。包括堆栈和内存的基本知识。常见和堆栈相关的 bug,如栈溢出,内存泄漏,堆内存分配失败等。后面介绍软件中堆栈统计的重要性,以及如何使用工具工具软件中堆栈使用的范围,并给出在软件开发中,如何降低堆栈问题,优化堆栈的一些实践。
    的头像 发表于 08-29 14:10 459次阅读
    堆栈和内存的<b class='flag-5'>基本知识</b>

    光刻工艺的基本知识

    在万物互联,AI革命兴起的今天,半导体芯片已成为推动现代社会进步的心脏。而光刻(Lithography)威廉希尔官方网站 ,作为先进制造中最为精细和关键的工艺,不管是半导体芯片、MEMS器件,还是微纳光学元件都离不开光刻工艺的参与,其重要性不言而喻。本文将带您一起认识光刻工艺的基本知识
    的头像 发表于 08-26 10:10 805次阅读
    光刻工艺的<b class='flag-5'>基本知识</b>

    探索虚拟线程:原理与实现

    虚拟线程的引入与优势 在Loom项目之前,Java虚拟机(JVM)中的线程是通过java.lang.Thread类型来实现的,这些线程被称为平台线程。 然而,平台
    的头像 发表于 06-24 11:35 294次阅读
    探索虚拟<b class='flag-5'>线程</b>:原理与实现

    鸿蒙开发:【线程模型】

    管理其他线程的ArkTS引擎实例,例如使用TaskPool(任务池)创建任务或取消任务、启动和终止Worker线程
    的头像 发表于 06-13 16:38 398次阅读
    鸿蒙开发:【<b class='flag-5'>线程</b>模型】

    热释电传感器基本知识和使用中的常见问题

    电子发烧友网站提供《热释电传感器基本知识和使用中的常见问题》资料免费下载
    发表于 06-11 09:06 1次下载

    java实现多线程的几种方式

    Java实现多线程的几种方式 多线程是指程序中包含了两个或以上的线程,每个线程都可以并行执行不同的任务或操作。Java中的多线程可以提高程序
    的头像 发表于 03-14 16:55 693次阅读

    python中5种线程锁盘点

    线程安全是多线程或多进程编程中的一个概念,在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确
    发表于 03-07 11:08 1587次阅读
    python中5种<b class='flag-5'>线程</b>锁盘点

    什么是动态线程池?动态线程池的简单实现思路

    因此,动态可监控线程池一种针对以上痛点开发的线程池管理工具。主要可实现功能有:提供对 Spring 应用内线程池实例的全局管控、应用运行时动态变更线程池参数以及
    的头像 发表于 02-28 10:42 641次阅读

    linux多线程编程实例

    linux线程
    的头像 发表于 02-15 21:16 461次阅读
    linux多<b class='flag-5'>线程</b>编程实例

    线程是什么的基本单位 进程与线程的本质区别

    线程是操作系统中处理器调度的基本单位,它代表着独立的执行流。在一个进程中,可以包含多个线程,这些线程共享相同的进程资源,如内存空间、文件描述符等。 进程是操作系统中运行的程序的实例,它包含了程序
    的头像 发表于 02-02 16:30 927次阅读

    什么是守护线程?守护线程的底层原理和使用示例

    大家好,今天这篇文章来梳理一下有关守护线程的相关问题,这也是之前曾经有被问到过的面试题,在此之前我们先看一看守护线程的使用示例。
    的头像 发表于 01-05 11:01 1413次阅读
    什么是守护<b class='flag-5'>线程</b>?守护<b class='flag-5'>线程</b>的底层原理和使用示例

    mcu线程和进程的区别是什么

    MCU线程和进程是嵌入式系统中常见的并行执行的概念,它们之间有许多区别,包括线程与进程的定义、资源管理、通信机制、执行方式等等。下面将详细介绍MCU线程和进程的区别。 一、定义与概念 MCU线
    的头像 发表于 01-04 10:45 745次阅读