【STM32RT-Thread零基础入门】 5. 线程创建应用(线程创建、删除、初始化、脱离、启动、睡眠)

news2024/10/6 8:23:31

硬件:STM32F103ZET6、ST-LINK、usb转串口工具、4个LED灯、1个蜂鸣器、4个1k电阻、2个按键、面包板、杜邦线

文章目录

  • 前言
  • 一、线程管理接口介绍
  • 二、任务:使用多线程的方式同时实现led闪烁和按键控制喇叭(扫描法)
    • 1. RT-Thread相关接口函数
      • (1)创建和删除线程
      • (2)初始化和脱离线程
      • (3)启动线程
      • (4)线程睡眠
    • 2. 代码实现
      • (1)led灯闪烁功能模块实现
      • (2)按键控制喇叭功能模块实现
      • (3)main()程序设计
    • 3. 程序测试
  • 总结


前言

本章主要讲线程的工作机制和管理方法,通过实例讲解如何使用多线程完成多任务开发。


一、线程管理接口介绍

RT-Thread用线程控制块来描述和管理一个线程,一个线程对应一个线程控制块。线程控制块由结构体struct rt_thread表示,它用于存放线程的所有一些信息,例如优先级、线程名称、线程状态等,也包含线程与线程之间连接用的链表结构,线程等待事件集合等,详细定义如下:

/* 线程控制块 */
struct rt_thread
{
    /* rt 对象 */
    char        name[RT_NAME_MAX];     /* 线程名称 */
    rt_uint8_t  type;                   /* 对象类型 */
    rt_uint8_t  flags;                  /* 标志位 */

    rt_list_t   list;                   /* 对象列表 */
    rt_list_t   tlist;                  /* 线程列表 */

    /* 栈指针与入口指针 */
    void       *sp;                      /* 栈指针 */
    void       *entry;                   /* 入口函数指针 */
    void       *parameter;              /* 参数 */
    void       *stack_addr;             /* 栈地址指针 */
    rt_uint32_t stack_size;            /* 栈大小 */

    /* 错误代码 */
    rt_err_t    error;                  /* 线程错误代码 */
    rt_uint8_t  stat;                   /* 线程状态 */

    /* 优先级 */
    rt_uint8_t  current_priority;    /* 当前优先级 */
    rt_uint8_t  init_priority;        /* 初始优先级 */
    rt_uint32_t number_mask;

    ......

    rt_ubase_t  init_tick;               /* 线程初始化计数值 */
    rt_ubase_t  remaining_tick;         /* 线程剩余计数值 */

    struct rt_timer thread_timer;      /* 内置线程定时器 */

    void (*cleanup)(struct rt_thread *tid);  /* 线程退出清除函数 */
    rt_uint32_t user_data;                      /* 用户数据 */
};

  • init_priority 是线程创建时指定的线程优先级,在线程运行过程当中是不会被改变的(除非用户执行线程控制函数进行手动调整线程优先级)。
  • cleanup 会在线程退出时,被空闲线程回调一次以执行用户设置的清理现场等工作。
  • user_data 可由用户挂接一些数据信息到线程控制块中,以提供一种类似线程私有数据的实现方式。

线程的创建、启动、删除等操作,都与线程控制块相关,RT-Thread提供线程管理和控制的一些函数,具体如下:

  • rt_thread_create() 创建线程
  • rt_thread_delete() 删除线程
  • rt_thread_init() 初始化线程
  • rt_thread_detach() 脱离线程
  • rt_thread_startup() 启动线程
  • rt_thread_self() 活得当前正在运行的线程,返回值位rt_thread_t类型(线程的句柄)
  • rt_thread_yield() 使线程让出处理器资源
  • rt_thread_sleep() 使线程睡眠指定tick,调用该函数,线程会被挂起
  • rt_thread_delay() 使线程延迟指定tick,调用该函数,线程会被挂起
  • rt_thread_mdelay() 使线程睡眠指定毫秒,调用该函数,线程会被挂起
  • rt_thread_suspend() 挂起线程
  • rt_thread_resume() 恢复线程
  • rt_thread_control() 控制线程

二、任务:使用多线程的方式同时实现led闪烁和按键控制喇叭(扫描法)

任务描述:在main线程中实现LED灯闪烁的功能,同时创建一个线程单独实现按键控制喇叭的功能,按键的识别使用扫描法。另外,要求可以通过命令控制灯的闪烁模式。硬件电路如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1. RT-Thread相关接口函数

(1)创建和删除线程

一个线程要成为可执行的对象,就必须由操作系统的内核来为它创建一个线程。线程可以动态创建和删除。

① 创建线程

可以通过如下的接口创建一个动态线程:

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);

调用这个函数时,系统会从动态堆内存中分配一个线程句柄以及按照参数中指定的栈大小从动态堆内存中分配相应的空间。分配出来的栈空间是按照 rtconfig.h 中配置的 RT_ALIGN_SIZE 方式对齐。

线程创建rt_thread_create() 的参数和返回值见下表:

参数描述
name线程的名称;线程名称的最大长度由 rtconfig.h 中的宏 RT_NAME_MAX 指定,多余部分会被自动截掉
entry线程入口函数
parameter线程入口函数参数
stack_size线程栈大小,单位是字节
priority线程的优先级。优先级范围根据系统配置情况(rtconfig.h 中的 RT_THREAD_PRIORITY_MAX 宏定义),如果支持的是 256 级优先级,那么范围是从 0~255,数值越小优先级越高,0 代表最高优先级
tick线程的时间片大小。时间片(tick)的单位是操作系统的时钟节拍。当系统中存在相同优先级线程时,这个参数指定线程一次调度能够运行的最大时间长度。这个时间片运行结束时,调度器自动选择下一个就绪态的同优先级线程进行运行
返回Thread:线程创建成功,返回线程句柄;RT_NULL:线程创建失败

② 删除线程

对于一些使用 rt_thread_create() 创建出来的线程,当不需要使用,或者运行出错时,我们可以使用下面的函数接口来从系统中把线程完全删除掉:

rt_err_t rt_thread_delete(rt_thread_t thread);

调用该函数后,线程对象将会被移出线程队列并且从内核对象管理器中删除,线程占用的堆栈空间也会被释放,收回的空间将重新用于其他的内存分配。
实际上,用 rt_thread_delete() 函数删除线程接口,仅仅是把相应的线程状态更改为 RT_THREAD_CLOSE 状态,然后放入到 rt_thread_defunct 队列中;
而真正的删除动作(释放线程控制块和释放线程栈)需要到下一次执行空闲线程时,由空闲线程完成最后的线程删除动作。
线程删除 rt_thread_delete() 接口的参数和返回值见下表:

参数描述
thread要删除的线程句柄
返回RT_EOK 删除线程成功,-RT_ERROR 删除线程失败

注:rt_thread_create() 和 rt_thread_delete() 函数仅在使能了系统动态堆时才有效(即 RT_USING_HEAP 宏定义已经定义了)。

(2)初始化和脱离线程

静态方法使用线程

① 初始化线程

线程的初始化可以使用下面的函数接口完成,来初始化静态线程对象:

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);

静态线程的线程句柄(或者说线程控制块指针)、线程栈由用户提供。

静态线程是指线程控制块、线程运行栈一般都设置为全局变量,在编译时就被确定、被分配处理,内核不负责动态分配内存空间。

需要注意的是,用户提供的栈首地址需做系统对齐(例如 ARM 上需要做 4 字节对齐)。线程初始化接口 rt_thread_init() 的参数和返回值见下表:

参数描述
thread线程句柄。线程句柄由用户提供出来,并指向对应的线程控制块内存地址
name线程的名称;线程名称的最大长度由 rtconfig.h 中定义的 RT_NAME_MAX 宏指定,多余部分会被自动截掉
entry线程入口函数
parameter线程入口函数参数
stack_start线程栈起始地址
stack_size线程栈大小,单位是字节。在大多数系统中需要做栈空间地址对齐(例如 ARM 体系结构中需要向 4 字节地址对齐)
priority线程的优先级。优先级范围根据系统配置情况(rtconfig.h 中的 RT_THREAD_PRIORITY_MAX 宏定义),如果支持的是 256 级优先级,那么范围是从 0 ~ 255,数值越小优先级越高,0 代表最高优先级
tick线程的时间片大小。时间片(tick)的单位是操作系统的时钟节拍。当系统中存在相同优先级线程时,这个参数指定线程一次调度能够运行的最大时间长度。这个时间片运行结束时,调度器自动选择下一个就绪态的同优先级线程进行运行
返回RT_EOK 线程创建成功,-RT_ERROR 线程创建失败

② 脱离线程

对于用 rt_thread_init() 初始化的线程,使用 rt_thread_detach() 将使线程对象在线程队列和内核对象管理器中被脱离。线程脱离函数如下:

rt_err_t rt_thread_detach (rt_thread_t thread);

线程脱离接口 rt_thread_detach() 的参数和返回值见下表:

参数描述
thread线程句柄,它应该是由rt_thread_init进行初始化的线程句柄
返回RT_EOK: 线程脱离成功,-RT_ERROR: 线程脱离失败

**这个接口函数与 rt_thread_delete() 函数相对应, rt_thread_delete() 函数操作的对象是 rt_thread_create()创建的句柄,而 rt_thread_detach()函数操作的对象是 rt_thread_init()初始化的线程控制块。**同样线程本身不应调用这个接口脱离线程本身。

(3)启动线程

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

rt_err_t rt_thread_startup(rt_thread_t thread);

当调用这个函数时,将把线程的状态更改为就绪状态,并放到相应优先级队列中等待调度。如果新启动的线程优先级比当前线程优先级高,将立刻切换到这个线程。线程启动接口 rt_thread_startup() 的参数和返回值见下表:

参数描述
thread线程句柄
返回RT_EOK:线程启动成功,-RT_ERROR 线程启动失败

(4)线程睡眠

在实际应用中,我们有时需要让运行的当前线程延迟一段时间,在指定的时间到达后重新运行,这就叫做 “线程睡眠”。线程睡眠可使用以下三个函数接口:

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_sleep/delay/mdelay() 的参数和返回值见下表:

参数描述
tick/ms线程睡眠的时间:sleep/delay 的传入参数 tick 以 1 个 OS Tick 为单位 ;mdelay 的传入参数 ms 以 1ms 为单位;
返回RT_EOK: 操作成功

2. 代码实现

本任务主要实现两个功能:灯闪烁、按键控制喇叭。这个两个功能都可以采用循环结构来设计,但是在第3章的任务中发现,单线程中采用循环结构同时实现这两个功能,在代码实现上比较困难,所以本任务分两个线程来实现:一个线程实现灯闪烁,另一个线程实现按键扫描并控制喇叭开关。
构建两个小功能模块,分别是灯闪烁和按键控制喇叭。

(1)led灯闪烁功能模块实现

car_led.h文件主要用于实现变量类型的定义和函数的声明,代码如下:

#ifndef APPLICATIONS_CAR_LED_H_
#define APPLICATIONS_CAR_LED_H_

/* 枚举方式定义车灯闪烁模式 */
enum led_mode {
    LED_MODE_STOP=0, //停止闪烁
    LED_MODE_Double, //双闪
    LED_MODE_LEFT,   //左灯闪烁
    LED_MODE_RIGHT   //右灯闪烁
};
void led_thread_entry();               //线程入口函数声明
void led_set_mode(enum led_mode m);    //车灯闪烁模式设置声明

#endif /* APPLICATIONS_CAR_LED_H_ */

car_led.c是具体的函数实现,本文件除了实现闪灯线程的入口函数以外,还按任务要求,实现闪灯模式设置接口,并把设置接口导出到msh命令列表中,导出后,用户就可以通过在终端输入命令来改变闪烁的模式,代码如下:

#include <rtthread.h>
#include <rtdevice.h>
#include "drv_common.h"

#define DBG_TAG "LED"    //定义日志打印标签
#define DBG_LVL DBG_LOG  //定义日志打印级别
/* 包含日志打印头文件。注意,以上2个宏定义一定要在这个头文件前定义才有效*/
#include <rtdbg.h> 
#include <stdlib.h>      //atoi()函数需要包含的头文件
#include "car_led.h"
/* 定义左右转向灯的控制引脚 */
#define LedLeft GET_PIN(D, 8)
#define LedRight GET_PIN(D, 9)
/* 定义开灯/关灯接口 */
#define LedOn(pin) rt_pin_write(pin, PIN_LOW)
#define LedOff(pin) rt_pin_write(pin, PIN_HIGH)

enum led_mode LedMod=LED_MODE_STOP; //车灯默认为不闪烁

/* 设置闪灯模式的接口 */
void led_set_mode(enum led_mode m)   
{
    LedMod = m;
}

/* 车灯控制线程入口函数定义 */
void led_thread_entry()
{
    /*设置车灯控制引脚模式为输出模式*/
    rt_pin_mode(LedLeft, PIN_MODE_OUTPUT);
    rt_pin_mode(LedRight, PIN_MODE_OUTPUT);
    while(1){
        switch(LedMod)//判断模式
        {
        case LED_MODE_STOP://停止闪烁
            LedOff(LedLeft);
            LedOff(LedRight);
            break;
        case LED_MODE_Double://双闪
            LedOn(LedLeft);
            LedOn(LedRight);
            rt_thread_mdelay(500);
            LedOff(LedLeft);
            LedOff(LedRight);
            break;
        case LED_MODE_LEFT://左灯闪
            LedOff(LedRight);
            LedOn(LedLeft);
            rt_thread_mdelay(250);
            LedOff(LedLeft);
            break;
        case LED_MODE_RIGHT://右灯闪
            LedOff(LedLeft);
            LedOn(LedRight);
            rt_thread_mdelay(250);
            LedOff(LedRight);
            break;
        default:
            LOG_D("mode error\n");
        }
        rt_thread_mdelay(250);//此处延时主要是为了让出CPU,不能让线程一直占用CPU
    }
}
/*msh命令接口*/
void ledmode(int argn, char *argv[])
{
    if(argn<2){
        LOG_W("ledmode #mode");
        return ;
    }
    led_set_mode(atoi(argv[1]));//通过atoi函数把字符变量转为整型变量
}
/* 导出到 msh 命令列表中,MSH_CMD_EXPORT(command,desc),其中command为函数名字,desc为用于描述命令用法或功能的字符串,可以自定 */
MSH_CMD_EXPORT(ledmode, set led flash mode);

(2)按键控制喇叭功能模块实现

扫描法进行按键扫描。
car_beep.h代码如下:

#ifndef APPLICATIONS_CAR_BEEP_H_
#define APPLICATIONS_CAR_BEEP_H_
void beep_thread_entry();//按键扫描并控制蜂鸣器的接口声明
#endif /* APPLICATIONS_CAR_BEEP_H_ */

car_beep.c

#include <rtthread.h>
#include <rtdevice.h>
#include "drv_common.h"
#define DBG_TAG "BEEP"     //定义日志打印标签
#define DBG_LVL DBG_LOG    //定义日志打印级别
#include <rtdbg.h>         //以上2个宏定义一定要在这个头文件前定义才有效
#define KeyBeep GET_PIN(A, 0)  //按键引脚定义
#define Beep GET_PIN(A, 5)     //蜂鸣器引脚定义
/*按键扫描并控制蜂鸣器的接口实现*/
int beep_thread_entry(void)
{
    /* 把蜂鸣器引脚设置为推拉模式 */
    rt_pin_mode(Beep, PIN_MODE_OUTPUT);
    /* 把按键引脚设置为上拉输入模式 */
    rt_pin_mode(KeyBeep, PIN_MODE_INPUT_PULLUP);

    while (1)
    {
        if(PIN_LOW==rt_pin_read(KeyBeep)){//按键按下
            rt_thread_mdelay(20);//延时去抖
            if(PIN_LOW==rt_pin_read(KeyBeep))
                rt_pin_write(Beep, PIN_LOW);//蜂鸣器响
        }
        else
            rt_pin_write(Beep, PIN_HIGH);//否则,蜂鸣器不响

        rt_thread_mdelay(100);//每0.1秒进行一次按键扫描,线程要有让出CPU的时候
    }

    return RT_EOK;
}

注:本任务,两个线程都采用了无线循环模式,为了不让线程陷入死循环操作,两个线程都在每次循环后通过调用延时函数来让出CPU使用权。但这种让出方式还不是最好的方式,通常可以通过让线程灯带信号量的方式使线程挂起,从而达到在线程空闲时让出CPU的效果,具体参考后面线程同步相关章节。

(3)main()程序设计

main()程序主要实现线程的创建,在本任务中,我们分别采用动态方法和静态方法来创建线程,主要是方便了解两种方法的使用,具体代码如下:

#include <rtthread.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include "car_led.h"    //包含LED控制模块头文件
#include "car_beep.h"   //包含蜂鸣器控制模块头文件

#define THREAD_STACK_SIZE   1024   //定义线程栈大小
#define THREAD_PRIORITY     20     //定义线程优先级
#define THREAD_TIMESLICE    10     //定义线程时间片

/* 栈首地址必须系统对齐 */
ALIGN(RT_ALIGN_SIZE)
static char beep_stack[THREAD_STACK_SIZE];  //定义栈空间
static struct rt_thread beepThread;    //静态方式定义beep线程控制块
rt_thread_t TidLed = RT_NULL;  //动态方式定义LED线程句柄

int main(void)
{
    int ret;

    /* 动态方式创建线程 */
    TidLed = rt_thread_create("LED", 
								led_thread_entry, 
								RT_NULL,
								THREAD_STACK_SIZE, 
								THREAD_PRIORITY, 
								THREAD_TIMESLICE);
    if (TidLed != RT_NULL)//判断线程是否成功创建
        rt_thread_startup(TidLed);//成功则启动线程
    else {//否则打印日志并即出
        LOG_D("can not create LED thread!");
        return -1;
    }

    /* 采用静态方式初始化线程 */
    ret = rt_thread_init(&beepThread,
                            "BEEP",
                            beep_thread_entry,
                            RT_NULL,
                            &beep_stack[0],
                            sizeof(beep_stack),
                            THREAD_PRIORITY,
                            THREAD_TIMESLICE);
    if (ret == RT_EOK) //判断线程是否成功创建
        rt_thread_startup(&beepThread); //成功则启动线程
    else { //否则打印日志并即出
        LOG_D("can not init beep thread!");
        return -1;
    }

    return RT_EOK;
}
void stop_led_thread(void)//删除led线程命令
{
    rt_thread_delete(TidLed);//动态创建的线程用delete删除
}
void stop_beep_thread(void) //删除beep线程命令
{
    rt_thread_detach(&beepThread);//静态初始化的线程用detach删除
}

/* 导出到 msh 命令列表中*/
MSH_CMD_EXPORT(stop_led_thread, delete led thread);
MSH_CMD_EXPORT(stop_beep_thread, delete beep thread);

复制以上代码文件到applicastions目录中,构建并下载程序到开发板。

3. 程序测试

(1)系统启动后,两个LED等常亮无闪烁,终端输入ps命令查看系统线程情况,发现系统中新增了2个线程,分别使BEEP和LED,如图所示
在这里插入图片描述
(2)在终端中输入help命令查看系统命令支持情况,发现系统新增了程序中导出的3个命令ledmode、stop_led_thread、stop_beep_thread,如下图所示:
在这里插入图片描述
(3)终端输入ledmode 1、ledmode 2、ledmode 3等命令后,观察到车灯闪烁分别变为“双闪”“左灯闪”“右灯闪”。
在这里插入图片描述
(4)按下按键后,喇叭发出响声。
(5)松开按键后,喇叭停止发声。
(6)一直按住按键不松开,喇叭发出响声的同时,车灯继续闪烁。
(7)通过终端输入停止车灯闪烁命令stop_led_thread,观察车灯不再闪烁;通过ps命令查看系统线程情况,发现系统中不存在LED线程了

在这里插入图片描述

(8)通过终端输入停止喇叭响声命令stop_beep_thread,按下按键时喇叭不响;通过ps命令查看系统线程情况,发现系统中不存在BEEP线程了

在这里插入图片描述


总结

本文讲了线程创建的应用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/885814.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

根据两组相关联数据(部门 + 用户),生成列表树

文章目录 背景想要得到的数据格式业务层获取数据&#xff0c;部门及用户&#xff0c;构建树结构TreeUtil生成的格式部门实体用户实体 背景 目前拥有用户和部门两组数据&#xff0c;根据部门和用户的关系&#xff0c;生成部门树&#xff0c;且每个部门下拥有哪些与子部门同级的…

接口自动化测试框架搭建

为什么要做&#xff08;自动化&#xff09;接口测试&#xff1f; 1、由于现在各个系统的复杂度不断上升&#xff0c;导致传统的测试方法成本上升且测试效率大幅下降&#xff0c;而接口测试相对于UI测试更加稳定&#xff0c;且相对容易实现自动化持续集成&#xff0c;可以减少人…

Spring BeanDefinition 也分父子关系?

在 Spring 框架中&#xff0c;BeanDefinition 是一个核心概念&#xff0c;用于定义和配置 bean 的元数据&#xff0c;虽然在实际应用中&#xff0c;我们一般并不会或者很少直接定义 BeanDefinition&#xff0c;但是&#xff0c;我们在 XML 文件中所作的配置&#xff0c;以及利用…

数字化智慧工地云平台,劳务实名制系统、视频监控系统、环境监测系统、人员定位系统、工资代发系统、AI识别系统、视频监控系统

智慧工地概念 智慧工地是一种崭新的工程全生命周期管理理念&#xff0c;是指运用信息化手段&#xff0c;通过对工程项目进行精确设计和施工模拟&#xff0c;围绕施工过程管理&#xff0c;建立互联协同、智能生产、科学管理的施工项目信息化生态圈&#xff0c;并将此数据在虚拟…

RabbitMQ的5种消息队列

RabbitMQ的5种消息队列 1、七种模式介绍与应用场景 1.1 简单模式(Hello World) 一个生产者对应一个消费者&#xff0c;RabbitMQ 相当于一个消息代理&#xff0c;负责将 A 的消息转发给 B。 应用场景&#xff1a;将发送的电子邮件放到消息队列&#xff0c;然后邮件服务在队列…

LeetCode150道面试经典题-- 环形链表(简单)

1.题目 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置&…

通过pycharm使用git和github的步骤(图文详解)

一、在Pycharm工具中配置集成Git和GitHub。 1.集成Git。 打开Pycharm, 点击File-->Settins-->Version Control-->Git 然后在 Path to Git executable中选择本地的git.exe路径。如下图&#xff1a; 2.集成GitHub 打开Pycharm, 点击File-->Settins-->Version…

基于Googlenet深度学习网络的信号调制类型识别matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 深度学习与卷积神经网络 4.2 数据预处理 4.3 GoogLeNet结构 4.4 分类器 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022a 3.部分核心程序 ............…

Qt 父子对象的关系

文章目录 前言一. Qt 对象可以存在父子关系&#xff1a;二. 父子关系的建立&#xff1a;三. Qt 对象的销毁&#xff1a; 总结 前言 Qt是一个流行的C框架&#xff0c;用于开发跨平台的图形用户界面&#xff08;GUI&#xff09;应用程序。Qt提供了一种强大的对象模型&#xff0c…

leetcode做题笔记83删除排序链表中的重复元素

给定一个已排序的链表的头 head &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只出现一次 。返回 已排序的链表 。 输入&#xff1a;head [1,1,2] 输出&#xff1a;[1,2] 思路一&#xff1a;模拟题意 struct ListNode* deleteDuplicates(struct ListNode* head){i…

韦东山-电子量产工具项目:显示单元

所有代码都已通过测试跑通&#xff0c;其中代码结构如下&#xff1a; 一、include文件夹 1.1 disp_manager.h #ifndef _DISP_MANAGER_H //防止头文件重复包含,只要右边的出现过&#xff0c;就不会再往下编译 #define _DISP_MANAGER_H //区域结构体 typedef struct DispBuff …

机器学习样本数据划分的典型Python方法

机器学习样本数据划分的典型Python方法 DateAuthorVersionNote2023.08.16Dog TaoV1.0完成文档撰写。 文章目录 机器学习样本数据划分的典型Python方法样本数据的分类Training DataValidation DataTest Data numpy.ndarray类型数据直接划分交叉验证基于KFold基于RepeatedKFold基…

【C++】面向对象编程引入 ( 面向过程编程 | 查看 iostream 依赖 | 面向对象编程 )

文章目录 一、面向过程编程二、查看 iostream 依赖三、面向对象编程 一、面向过程编程 给定 圆 的 半径 , 求该圆 的 周长 和 面积 ; 半径为 r r r , 周长就是 2 π r 2 \pi r 2πr , 面积是 π r 2 \pi r^2 πr2 ; 使用 面向过程 的方法解决上述问题 , 只能是令程序顺序执…

DDCX——运维开发准备

DD——运维开发准备 一4 linux用的什么版本&#xff0c;常见命令&#xff08;awk sed grep telnet netstate tcpdump top ps perf&#xff09;5 数据库有哪些类型&#xff0c;关系型数据库有哪些&#xff0c;非关系型数据库有哪些6 mysql事务7 mysql集群了解多少8 redis数据类型…

Spring 框架入门介绍及IoC的三种注入方式

目录 一、Spring 简介 1. 简介 2. spring 的核心模块 ⭐ 二、IoC 的概念 2.1 IoC 详解 2.2 IoC的好处 2.3 谈谈你对IoC的理解 三、IoC的三种注入方式 3.1 构造方法注入 3.2 setter方法注入 3.3 接口注入&#xff08;自动分配&#xff09; 3.4 spring上下文与tomcat整…

ChatGPT聊天微信小程序源码/适配H5和WEB端

ChatGPT-MP(基于ChatGPT实现的微信小程序&#xff0c;适配H5和WEB端) 可二开包含前后台&#xff0c;支持打字效果输出流式输出&#xff0c;支持AI聊天次数限制&#xff0c;支持分享增加次数等功能。开源版禁止商用&#xff0c;仅供学习交流&#xff0c;禁止倒卖。 技术栈&…

本地linux 搭建云服务器

本人穷逼&#xff0c;三年268 腾讯云可以接受&#xff0c;续费千百块 承担不起 研究了一会&#xff0c;发现搭建云服务器有两种较好的方式 一种是有公网IP的&#xff0c;另外是没有公网IP的&#xff0c;这里实验成功的是没有公网ip的方法 这种方法有缺点&#xff0c;因为fre…

前端组件高级封装技巧--纯干货

对于前端的小伙伴来说&#xff0c;最常见的工作就是写后台管理系统的页面&#xff0c;而后台管理系统最多的操作就是CRUD了&#xff0c;类似下面的&#xff0c;一个搜索框&#xff0c;一个表格&#xff0c;一个分页&#xff0c;然后点击新增编辑有个弹框 当你写过一段时间的CRU…

OpenDDS安装教程 Java开发

一、环境搭建 1、版本介绍 笔者使用以下版本&#xff08;不同版本的openDDS对应ACETAO版本不同&#xff09; openDDS&#xff1a;3.14 ACETAO&#xff1a;6.5.12 perl&#xff1a;5.32.0.1-64bit Visual Studio&#xff1a;Community 2019 jdk&#xff1a;jdk-8u111-windows-…

spark的standalone 分布式搭建

一、环境准备 集群环境hadoop11&#xff0c;hadoop12 &#xff0c;hadoop13 安装 zookeeper 和 HDFS 1、启动zookeeper -- 启动zookeeper(11,12,13都需要启动) xcall.sh zkServer.sh start -- 或者 zk.sh start -- xcall.sh 和zk.sh都是自己写的脚本-- 查看进程 jps -- 有…