【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属性,它指向函数的原型…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
Windows安装Miniconda
一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...
[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...
【LeetCode】算法详解#6 ---除自身以外数组的乘积
1.题目介绍 给定一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O…...
比特币:固若金汤的数字堡垒与它的四道防线
第一道防线:机密信函——无法破解的哈希加密 将每一笔比特币交易比作一封在堡垒内部传递的机密信函。 解释“哈希”(Hashing)就是一种军事级的加密术(SHA-256),能将信函内容(交易细节…...
OpenHarmony标准系统-HDF框架之I2C驱动开发
文章目录 引言I2C基础知识概念和特性协议,四种信号组合 I2C调试手段硬件软件 HDF框架下的I2C设备驱动案例描述驱动Dispatch驱动读写 总结 引言 I2C基础知识 概念和特性 集成电路总线,由串网12C(1C、12C、Inter-Integrated Circuit BUS)行数据线SDA和串…...
