【STM32RT-Thread零基础入门】 5. 线程创建应用(线程创建、删除、初始化、脱离、启动、睡眠)
硬件: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);//蜂鸣器响}elsert_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线程了

总结
本文讲了线程创建的应用
相关文章:
【STM32RT-Thread零基础入门】 5. 线程创建应用(线程创建、删除、初始化、脱离、启动、睡眠)
硬件:STM32F103ZET6、ST-LINK、usb转串口工具、4个LED灯、1个蜂鸣器、4个1k电阻、2个按键、面包板、杜邦线 文章目录 前言一、线程管理接口介绍二、任务:使用多线程的方式同时实现led闪烁和按键控制喇叭(扫描法)1. RT-Thread相关接…...
计算机竞赛 python+深度学习+opencv实现植物识别算法系统
0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 基于深度学习的植物识别算法研究与实现 🥇学长这里给一个题目综合评分(每项满分5分) 难度系数:4分工作量:4分创新点:4分 🧿 更多…...
深度探索ChatGPT:如何进行专业提问以获取精确答案
ChatGPT,作为OpenAI的先锋,已经展示出其惊人的交流和理解能力。但如何才能充分利用其潜能,并与之进行更深入、更专业的交流呢? 下面,我们将从专业的角度探讨一些提问策略,并附上实际案例,让你更加熟练地与…...
1.vue3+vite开发中axios使用及跨域问题解决
一、跨域问题解决 1.基于vitevue3配置时,在vite.congig.js文件server项目中添加 proxy代理 文件名:vite.congig.js server: {open: true,//启动项目自动弹出浏览器port: 3000,proxy: {/api: {target: http://localhost:8000/api/,changeOrigin: true,rew…...
【LangChain】P1 LangChain 应用程序的核心构建模块 LLMChain 以及其三大部分
LangChain 的核心构建模块 LLMChain LangChain 应用程序的核心构建模块语言模型 - LLMs提示模板 - Prompt templates输出解析器 - Output Parsers LLMChain 组合 LangChain 应用程序的核心构建模块 LangChain 应用程序的核心构建模块 LLMChain 由三部分组成: 语言…...
关于查看处理端口号和进程[linux]
查看端口号 lsof -i:端口号如果-bash: lsof: 未找到命令那我们可以执行yum install lsof 删除端口号进程 一般我们都会使用kill命令 kill -l#列出所有可用信号1 (HUP):重新加载进程。9 (KILL):杀死一个进程。15 (TERM):正常停止一个进程。 …...
C 语言的 strcat() 函数和 strncat() 函数
文章目录 strcat() 函数strncat() 函数 strcat() 函数 原型: char *strcat(char *dest, const char *src) 参数: dest – 指向目标数组,该数组包含了一个 C 字符串,且足够容纳追加后的字符串。 src – 指向要追加的字符串,该字符串不会覆…...
C++ string 的用法
目录 string类string类接口函数及基本用法构造函数,析构函数及赋值重载函数元素访问相关函数operator[]atback和front 迭代器iterator容量操作size()和length()capacity()max_sizeclearemptyreserveresizeshrink_to_fit string类对象修改操作operatorpush_backappen…...
MyBatis-Flex学习记录1---请各位大神指教
简介(官网介绍) MyBatis-Flex 是一个优雅的 MyBatis 增强框架,它非常轻量、同时拥有极高的性能与灵活性。我们可以轻松的使用 Mybaits-Flex 链接任何数据库,其内置的 QueryWrapper帮助我们极大的减少了 SQL 编写的工作的同时&…...
二分查找旋转数组
已知整数数组nums,先按升序排序后,再旋转。旋转k位后,元素分别为nums[k],nums[k1]...nums[0]...nums[k-1]。请查找target 是否存在,如果存在返回所在索引;否则返回-1。假定nums没有重复的元素。 假定排序后的数组为{1…...
关于3D位姿旋转
一. 主动旋转和被动旋转 1. active rotation 主动旋转 站在坐标系的位置看旋转目标物:目标物主动发生旋转。 2. passive rotation 被动旋转 站在旋转目标物的位置看坐标系: 坐标系发生旋转,相当于目标物在坐标系内的位置被动地发生了旋转…...
解锁项目成功的关键:项目经理的结构化思维之道
1. 项目经理的核心职责 作为项目经理,我们的工作不仅仅是跟踪进度和管理团队。我们的角色在整个项目生命周期中都是至关重要的,从初始概念到最终交付。以下是项目经理的几个核心职责: 确保项目目标的清晰性项目的成功在很大程度上取决于其目…...
力扣974被K整除的子数组
同余定理 使用前缀和哈希表 由于可能是负数所以要进行修正:(sum%kk)%k class Solution { public:int subarraysDivByK(vector<int>& nums, int k) {unordered_map<int,int> hash;hash[0 % k] 1; //0 这个数的余数int sum 0, ret 0;for(auto x…...
简单认识Docker数据管理
文章目录 为何需要docker数据管理数据管理类型 一、数据卷二、数据卷容器三、容器互联 为何需要docker数据管理 因为数据写入后如果停止了容器,再开启数据就会消失,使用数据管理的数据卷挂载,实现了数据的持久化,重启数据还会存在…...
UDP数据报结构分析(面试重点)
在传输层中有UDP和TCP两个重要的协议,下面将针对UDP数据报的结构进行分析 UDP结构图示 UDP报头结构的分析 UDP报头有4个属性,分别是源端口,目的端口,UDP报文长度,校验和,它们都占16位2个字节,所…...
【Java 动态数据统计图】动态数据统计思路案例(动态,排序,数组)二(113)
需求: 有一个List<Map<String.Object>>,存储了区域的数据, 数据是根据用户查询条件进行显示的;所以查询的数据是动态的;按区域维度统计每个区域出现的次数,并且按照次数的大小排序(升序&#…...
C++进阶 类型转换
本文简介:介绍C中类型转换的方式 类型转换 C语言中的类型转换为什么C需要四种类型转换C强制类型转换static_castreinterpret_castconst_castdynamic_cast RTTI(了解)总结 C语言中的类型转换 在C语言中,如果赋值运算符左右两侧类型…...
Idea中隐藏指定文件或指定类型文件
Setting ->Editor ->Code Style->File Types → Ignored Files and Folders输入要隐藏的文件名,支持*号通配符回车确认添加...
第2步---MySQL卸载和图形化工具展示
第2步---MySQL卸载和图形化工具展示 1.MySQL的卸载 2.MySQL的图形化工具 2.1常见的图形化工具 SQLyog:简单。SQLyog首页、文档和下载 - MySQL 客户端工具 - OSCHINA - 中文开源技术交流社区 Mysql Workbench :MySQL :: MySQL Workbench DataGrip&…...
原型和原型链
好久没记了有点忘记了,来记录一下。 1、函数和对象的关系:对象都是通过函数创建的,函数也是一个对象。 2、原型和原型链 1.原型:原型分为两种 prototype:每一个函数都会有prototype属性,它指向函数的原型…...
docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...
python爬虫——气象数据爬取
一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用: 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests:发送 …...
深入浅出Diffusion模型:从原理到实践的全方位教程
I. 引言:生成式AI的黎明 – Diffusion模型是什么? 近年来,生成式人工智能(Generative AI)领域取得了爆炸性的进展,模型能够根据简单的文本提示创作出逼真的图像、连贯的文本,乃至更多令人惊叹的…...
