9种单片机常用的软件架构

长文预警,加代码5000多字,写了4个多小时,盘软件架构,这篇文章就够了!

可能很多工程师,工作了很多年,都不会有软件架构的概念。

因为我在做研发工程师的第6年,才开始意识到这个东西,在此之前,都是做一些比较简单的项目,一个main函数干到底,架构复杂了反而是累赘。

后面有幸,接触了稍微复杂点的项目,感觉以前水平Hold不住,然后借着项目需求,学习了很多优秀的代码架构,比如以前同事的,一些模组厂的SDK,还有市面上成熟的系统。

说出来可能有点夸张,一个好项目带来的成长,顶你做几年小项目。

在一个工程师从入门到成为高级工程师,都会经历哪些软件架构?

下面给大家盘点一下,每个都提供了简易的架构模型代码。

1.线性架构

这是最简单的一种程序设计方法,也就是我们在入门时写的,下面是一个使用C语言编写的线性架构示例:

#include <reg51.h>  // 包含51系列单片机的寄存器定义// 延时函数,用于产生一定的延迟
void delay(unsigned int count) {unsigned int i;while(count--) {for(i = 0; i < 120; i++) {}  // 空循环,用于产生延迟}
}void main() {// 初始设置P1端口为输出模式,用于控制LEDP1 = 0xFF;  // 将P1端口设置为高电平,关闭所有LEDwhile(1) {  // 无限循环P1 = 0x00;  // 将P1端口设置为低电平,点亮所有LEDdelay(500000);  // 调用延时函数,延迟一段时间P1 = 0xFF;  // 将P1端口设置为高电平,关闭所有LEDdelay(500000);  // 再次调用延时函数,延迟相同的时间}
}

2.模块化架构

模块化架构是一种将程序分解为独立模块的设计方法,每个模块执行特定的任务。

这种架构有助于代码的重用、维护和测试。

下面是一个使用C语言编写的模块化架构示例,该程序模拟了一个简单的交通信号灯控制系统。

#include <reg51.h>  // 包含51系列单片机的寄存器定义// 定义信号灯的状态
typedef enum {RED_LIGHT,YELLOW_LIGHT,GREEN_LIGHT
} TrafficLightState;// 函数声明
void initializeTrafficLight(void);
void setTrafficLight(TrafficLightState state);
void delay(unsigned int milliseconds);// 信号灯控制主函数
void main(void) {initializeTrafficLight();  // 初始化交通信号灯while(1) {setTrafficLight(RED_LIGHT);delay(5000);  // 红灯亮5秒setTrafficLight(YELLOW_LIGHT);delay(2000);  // 黄灯亮2秒setTrafficLight(GREEN_LIGHT);delay(5000);  // 绿灯亮5秒}
}// 初始化交通信号灯的函数
void initializeTrafficLight(void) {// 这里可以添加初始化代码,比如设置端口方向、默认状态等// 假设P1端口连接了信号灯,初始状态为熄灭(高电平)P1 = 0xFF;
}// 设置交通信号灯状态的函数
void setTrafficLight(TrafficLightState state) {switch(state) {case RED_LIGHT:// 设置红灯亮,其他灯灭P1 = 0b11100000;  // 假设低电平有效,这里设置P1.0为低电平,其余为高电平break;case YELLOW_LIGHT:// 设置黄灯亮,其他灯灭P1 = 0b11011000;  // 设置P1.1为低电平,其余为高电平break;case GREEN_LIGHT:// 设置绿灯亮,其他灯灭P1 = 0b11000111;  // 设置P1.2为低电平,其余为高电平break;default:// 默认为熄灭所有灯P1 = 0xFF;break;}
}// 延时函数,参数是毫秒数
void delay(unsigned int milliseconds) {unsigned int delayCount = 0;while(milliseconds--) {for(delayCount = 0; delayCount < 120; delayCount++) {// 空循环,用于产生延时}}
}

3.层次化架构

层次化架构是一种将系统分解为多个层次的设计方法,每个层次负责不同的功能。

着以下是一个使用C语言编写的层次化架构示例,模拟了一个具有不同权限级别的嵌入式系统。

#include <reg51.h>  // 包含51系列单片机的寄存器定义// 定义不同的操作级别
typedef enum {LEVEL_USER,LEVEL_ADMIN,LEVEL_SUPERUSER
} OperationLevel;// 函数声明
void systemInit(void);
void performOperation(OperationLevel level);
void displayMessage(char* message);// 系统初始化后的主循环
void main(void) {systemInit();  // 系统初始化// 模拟用户操作performOperation(LEVEL_USER);// 模拟管理员操作performOperation(LEVEL_ADMIN);// 模拟超级用户操作performOperation(LEVEL_SUPERUSER);while(1) {// 主循环可以是空闲循环或者处理其他低优先级任务}
}// 系统初始化函数
void systemInit(void) {// 初始化系统资源,如设置端口、中断等// 这里省略具体的初始化代码
}// 执行不同级别操作的函数
void performOperation(OperationLevel level) {switch(level) {case LEVEL_USER://用户操作具体代码break;case LEVEL_ADMIN://管理员操作具体代码break;case LEVEL_SUPERUSER://超级用户操作具体代码break;}
}// 显示消息的函数
void displayMessage(char* message) {// 这里省略了实际的显示代码,因为单片机通常没有直接的屏幕输出// 消息可以通过LED闪烁、串口输出或其他方式展示// 假设通过P1端口的LED展示,每个字符对应一个LED闪烁模式// 实际应用中,需要根据硬件设计来实现消息的显示
}

4.事件驱动架构

事件驱动架构是一种编程范式,其中程序的执行流程由事件(如用户输入、传感器变化、定时器到期等)触发。

在单片机开发中,事件驱动架构通常用于响应外部硬件中断或软件中断。

以下是一个使用C语言编写的事件驱动架构示例,模拟了一个基于按键输入的LED控制。

#include <reg51.h>  // 包含51系列单片机的寄存器定义// 定义按键和LED的状态
#define KEY_PORT P3  // 假设按键连接在P3端口
#define LED_PORT P2  // 假设LED连接在P2端口// 函数声明
void delay(unsigned int milliseconds);
bit checkKeyPress(void);  // 返回按键是否被按下的状态(1表示按下,0表示未按下)// 定时器初始化函数
void timer0Init(void) 
{TMOD = 0x01;  // 设置定时器模式寄存器,使用模式1(16位定时器)TH0 = 0xFC;   // 设置定时器初值,用于产生定时中断TL0 = 0x18;ET0 = 1;      // 开启定时器0中断EA = 1;       // 开启总中断TR0 = 1;      // 启动定时器
}// 定时器中断服务程序
void timer0_ISR() interrupt 1 
{// 定时器溢出后自动重新加载初值,无需手动重置// 这里可以放置定时器溢出后需要执行的代码
}// 按键中断服务程序
bit keyPress_ISR(void) interrupt 2 using 1 
{if(KEY_PORT != 0xFF) // 检测是否有按键按下{  LED_PORT = ~LED_PORT;  // 如果有按键按下,切换LED状态delay(20);  // 去抖动延时while(KEY_PORT != 0xFF);  // 等待按键释放return 1;  // 返回按键已按下}return 0;  // 如果没有按键按下,返回0
}// 延时函数,参数是毫秒数
void delay(unsigned int milliseconds) {unsigned int i, j;for(i = 0; i < milliseconds; i++)for(j = 0; j < 1200; j++);  // 空循环,用于产生延时
}// 主函数
void main(void) 
{timer0Init();  // 初始化定时器LED_PORT = 0xFF;  // 初始LED熄灭(假设低电平点亮LED)while(1) {if(checkKeyPress()){  // 检查是否有按键按下事件// 如果有按键按下,这里可以添加额外的处理代码}}
}// 检查按键是否被按下的函数
bit checkKeyPress(void) 
{bit keyState = 0;// 模拟按键中断触发,实际应用中需要连接硬件中断if(1) // 假设按键中断触发{  keyState = keyPress_ISR();  // 调用按键中断服务程序}return keyState;  // 返回按键状态
}

事实上,真正的事件型驱动架构,是非常复杂的,我职业生涯的巅峰之作,就是用的事件型驱动架构。

5.状态机架构

在单片机开发中,状态机常用于处理复杂的逻辑和事件序列,如用户界面管理、协议解析等。

以下是一个使用C语言编写的有限状态机(FSM)的示例,模拟了一个简单的自动售货机的状态转换。

#include <reg51.h>  // 包含51系列单片机的寄存器定义// 定义自动售货机的状态
typedef enum {IDLE,COIN_INSERTED,PRODUCT_SELECTED,DISPENSE,CHANGE_RETURNED
} VendingMachineState;// 定义事件
typedef enum {COIN_EVENT,PRODUCT_EVENT,DISPENSE_EVENT,REFUND_EVENT
} VendingMachineEvent;// 函数声明
void processEvent(VendingMachineEvent event);
void dispenseProduct(void);
void returnChange(void);// 当前状态
VendingMachineState currentState = IDLE;// 主函数
void main(void)
{// 初始化代码(如果有)// ...while(1){// 假设事件由外部触发,这里使用一个模拟事件VendingMachineEvent currentEvent = COIN_EVENT; // 模拟投入硬币事件processEvent(currentEvent);  // 处理当前事件}
}// 处理事件的函数
void processEvent(VendingMachineEvent event)
{switch(currentState){case IDLE:if(event == COIN_EVENT){// 如果在空闲状态且检测到硬币投入事件,则转换到硬币投入状态currentState = COIN_INSERTED;}break;case COIN_INSERTED:if(event == PRODUCT_EVENT){// 如果在硬币投入状态且用户选择商品,则请求出货currentState = PRODUCT_SELECTED;}break;case PRODUCT_SELECTED:if(event == DISPENSE_EVENT){dispenseProduct();  // 出货商品currentState = DISPENSE;}break;case DISPENSE:if(event == REFUND_EVENT){returnChange();  // 返回找零currentState = CHANGE_RETURNED;}break;case CHANGE_RETURNED:// 等待下一个循环,返回到IDLE状态currentState = IDLE;break;default:// 如果状态非法,重置为IDLE状态currentState = IDLE;break;}
}// 出货商品的函数
void dispenseProduct(void)
{// 这里添加出货逻辑,例如激活电机推出商品// 假设P1端口连接了出货电机P1 = 0x00;  // 激活电机// ... 出货逻辑P1 = 0xFF;  // 关闭电机
}// 返回找零的函数
void returnChange(void)
{// 这里添加找零逻辑,例如激活机械臂放置零钱// 假设P2端口连接了找零机械臂P2 = 0x00;  // 激活机械臂// ... 找零逻辑P2 = 0xFF;  // 关闭机械臂
}

6.面向对象架构

STM32的库,就是一种面向对象的架构。

不过在单片机由于资源限制,OOP并不像在高级语言中那样常见,但是一些基本概念如封装和抽象仍然可以被应用。

虽然C语言本身并不直接支持面向对象编程,但可以通过结构体和函数指针模拟一些面向对象的特性。

下面是一个简化的示例,展示如何在C语言中模拟面向对象的编程风格,以51单片机为背景,创建一个简单的LED类。

#include <reg51.h>// 定义一个LED类
typedef struct {unsigned char state;  // LED的状态unsigned char pin;    // LED连接的引脚void (*turnOn)(struct LED*);  // 点亮LED的方法void (*turnOff)(struct LED*); // 熄灭LED的方法
} LED;// LED类的构造函数
void LED_Init(LED* led, unsigned char pin) {led->state = 0;  // 默认状态为熄灭led->pin = pin;   // 设置LED连接的引脚
}// 点亮LED的方法
void LED_TurnOn(LED* led) {// 根据引脚状态点亮LEDif(led->pin < 8) {P0 |= (1 << led->pin);  // 假设P0.0到P0.7连接了8个LED} else {P1 &= ~(1 << (led->pin - 8));  // 假设P1.0到P1.7连接了另外8个LED}led->state = 1;  // 更新状态为点亮
}// 熄灭LED的方法
void LED_TurnOff(LED* led) {// 根据引脚状态熄灭LEDif(led->pin < 8) {P0 &= ~(1 << led->pin);  // 熄灭P0上的LED} else {P1 |= (1 << (led->pin - 8));  // 熄灭P1上的LED}led->state = 0;  // 更新状态为熄灭
}// 主函数
void main(void) {LED myLed;  // 创建一个LED对象LED_Init(&myLed, 3);  // 初始化LED对象,连接在P0.3// 给LED对象绑定方法myLed.turnOn = LED_TurnOn;myLed.turnOff = LED_TurnOff;// 使用面向对象的风格控制LEDwhile(1) {myLed.turnOn(&myLed);  // 点亮LED// 延时myLed.turnOff(&myLed); // 熄灭LED// 延时}
}

这段代码定义了一个结构体LED,模拟面向对象中的“类。

这个示例仅用于展示如何在C语言中模拟面向对象的风格,并没有使用真正的面向对象编程语言的特性,如继承和多态,不过对于单片机的应用,足以。

7.基于任务的架构

这种我最喜欢用,结构,逻辑清晰,每个任务都能灵活调度。

基于任务的架构是将程序分解为独立的任务,每个任务执行特定的工作。

在单片机开发中,如果没有使用实时操作系统,我们可以通过编写一个简单的轮询调度器来模拟基于任务的架构。

以下是一个使用C语言编写的基于任务的架构的示例,该程序在51单片机上实现。

为了简化,我们将使用一个简单的轮询调度器来在两个任务之间切换:一个是按键扫描任务,另一个是LED闪烁任务。

#include <reg51.h>// 假设P1.0是LED输出
sbit LED = P1^0;// 全局变量,用于记录系统Tick
unsigned int systemTick = 0;// 任务函数声明
void taskLEDBlink(void);
void taskKeyScan(void);// 定时器0中断服务程序,用于产生Tick
void timer0_ISR() interrupt 1 using 1 
{// 定时器溢出后自动重新加载初值,无需手动重置systemTick++;  // 更新系统Tick计数器
}// 任务调度器,主函数中调用,负责任务轮询
void taskScheduler(void) 
{// 检查系统Tick,决定是否执行任务// 例如,如果我们需要每1000个Tick执行一次LED闪烁任务if (systemTick % 1000 == 0) {taskLEDBlink();}// 如果有按键任务,可以类似地检查Tick并执行if (systemTick % 10 == 0) {taskKeyScan();}
}// LED闪烁任务
void taskLEDBlink(void) 
{static bit ledState = 0;  // 用于记录LED的当前状态ledState = !ledState;  // 切换LED状态LED = ledState;         // 更新LED硬件状态
}// 按键扫描任务(示例中省略具体实现)
void taskKeyScan(void) 
{// 按键扫描逻辑
}// 主函数
void main(void) 
{// 初始化LED状态LED = 0;// 定时器0初始化设置TMOD &= 0xF0;  // 设置定时器模式寄存器,使用模式1(16位定时器/计数器)TH0 = 0x4C;     // 设置定时器初值,产生定时中断(定时周期取决于系统时钟频率)TL0 = 0x00;ET0 = 1;        // 允许定时器0中断EA = 1;         // 允许中断TR0 = 1;        // 启动定时器0while(1) {taskScheduler();  // 调用任务调度器}
}

这里只是举个简单的例子,这个代码示例,比较适合51和stm8这种资源非常少的单片机。

8.代理架构

这个大家或许比较少听到过,但在稍微复杂的项目中,是非常常用的。

在代理架构中,每个代理(Agent)都是一个独立的实体,它封装了特定的决策逻辑和数据,并与其他代理进行交互。

在实际项目中,需要创建多个独立的任务或模块,每个模块负责特定的功能,并通过某种机制(如消息队列、事件触发等)进行通信。

这种方式可以大大提高程序可扩展性和可移植性。

以下是一个LED和按键代理的简化模型。

#include <reg51.h>  // 包含51系列单片机的寄存器定义// 假设P3.5是按键输入,P1.0是LED输出
sbit KEY = P3^5;
sbit LED = P1^0;typedef struct 
{unsigned char pin;    // 代理关联的引脚void (*action)(void); // 代理的行为函数
} Agent;// 按键代理的行为函数声明
void keyAction(void);
// LED代理的行为函数声明
void ledAction(void);// 代理数组,存储所有代理的行为和关联的引脚
Agent agents[] = 
{{5, keyAction},  // 按键代理,关联P3.5{0, ledAction}   // LED代理,关联P1.0
};// 按键代理的行为函数
void keyAction(void) 
{if(KEY == 0) // 检测按键是否被按下{  LED = !LED;   // 如果按键被按下,切换LED状态while(KEY == 0);  // 等待按键释放}
}// LED代理的行为函数
void ledAction(void) 
{static unsigned int toggleCounter = 0;toggleCounter++;if(toggleCounter == 500)  // 假设每500个时钟周期切换一次LED{ LED = !LED;               // 切换LED状态toggleCounter = 0;        // 重置计数器}
}// 主函数
void main(void) 
{unsigned char agentIndex;// 主循环while(1) {for(agentIndex = 0; agentIndex < sizeof(agents) / sizeof(agents[0]); agentIndex++) {// 调用每个代理的行为函数(*agents[agentIndex].action)(); // 注意函数指针的调用方式}}
}

9.组件化架构

组件化架构是一种将软件系统分解为独立、可重用组件的方法。

将程序分割成负责特定任务的模块,如LED控制、按键处理、传感器读数等。

每个组件可以独立开发和测试,然后被组合在一起形成完整的系统。

以下是一个简化的组件化架构示例,模拟了一个单片机系统中的LED控制和按键输入处理两个组件。

为了简化,组件间的通信将通过直接函数调用来模拟。

#include <reg51.h>  // 包含51系列单片机的寄存器定义// 定义组件结构体
typedef struct 
{void (*init)(void);      // 组件初始化函数void (*task)(void);       // 组件任务函数
} Component;// 假设P3.5是按键输入,P1.0是LED输出
sbit KEY = P3^5;
sbit LED = P1^0;// LED组件
void LED_Init(void) 
{LED = 0;  // 初始化LED状态为关闭
}void LED_Task(void) 
{static unsigned int toggleCounter = 0;toggleCounter++;if (toggleCounter >= 1000) // 假设每1000个时钟周期切换一次LED{  LED = !LED;                // 切换LED状态toggleCounter = 0;         // 重置计数器}
}// 按键组件
void KEY_Init(void) 
{// 按键初始化代码
}void KEY_Task(void) 
{if (KEY == 0) // 检测按键是否被按下{  LED = !LED;  // 如果按键被按下,切换LED状态while(KEY == 0);  // 等待按键释放}
}// 组件数组,存储系统中所有组件的初始化和任务函数
Component components[] = 
{{LED_Init, LED_Task},{KEY_Init, KEY_Task}
};// 系统初始化函数,调用所有组件的初始化函数
void System_Init(void) 
{unsigned char componentIndex;for (componentIndex = 0; componentIndex < sizeof(components) / sizeof(components[0]); componentIndex++) {components[componentIndex].init();}
}// 主循环,调用所有组件的任务函数
void main(void) 
{System_Init();  // 系统初始化while(1) {unsigned char componentIndex;for (componentIndex = 0; componentIndex < sizeof(components) / sizeof(components[0]); componentIndex++){components[componentIndex].task();  // 调用组件任务}}
}

以上几种,我都整理到单片机入门到高级资料+工具包了,大家可自行在朋友圈找我安排。

当然,以上都是最简易的代码模型,如果想用于实际项目,很多细节还要优化。

后面为了适应更复杂的项目,我基于以上这几种编程思维,重构了代码,使OS变得移植性和扩展性更强,用起来也更灵活。

我在2019年,也系统录制过关于这套架构的教程,粉丝可找我安排。

目前我们无际单片机特训营项目3和6就是采用这种架构,稳的一批。

如果想系统提升编程思维和代码水平,还是得从0到1去学习我们项目,并不是说技术有多难,而是很多思维和实现细节,没有参考,没人指点,靠自己需要摸索很久。

除了以上架构,更复杂的就是RTOS了。

不过一般对于有架构设计能力的工程师来说,更习惯于使用传统的裸机编程方式,这种方式可能更直观且可控。

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

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

相关文章

unity 录制360全景渲染图

1.打开pakcageManager &#xff0c;选择packages为 unityRegisty&#xff0c;找到unityRecorder插件下载&#xff0c;点击右下角instant安装&#xff0c;如果插件列表为空&#xff0c;检查是否连接网络&#xff0c;重启Unity 2.打开录制面板 3.add recorder 选择ImageSequence …

steam上传包流程

steam接入完成之后&#xff0c;需要我们将包体上传到steam后台&#xff1b;此流程steam有提供专门的工具&#xff0c;就在 steam官网下载的sdk目录下&#xff08;sdk\tools&#xff09; 具体使用流程&#xff1a; 1、将打好的包放到sdk\tools\ContentBuilder\content目录下&…

【软测学习笔记】测试入门Day03

&#x1f31f;博主主页&#xff1a;我是一只海绵派大星 &#x1f4da;专栏分类&#xff1a;软件测试笔记 &#x1f4da;参考教程&#xff1a;黑马教程❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、缺陷 1、定义 2、缺陷标准 3、缺陷产生的原因 4、缺陷的生命周…

C++模板超详解

目录 一、了解什么是泛性编程 二、模版 1.函数模版 1.1 函数模板概念 1.2 函数模板格式 1.3 函数模板的原理 1.4 函数模板的实例化 1.5 模板参数的匹配原则 2.类模板 2.1 类模板的定义格式 2.2 类模板的实例化 3. 非类型模板参数 4. 模板的特化 4.1 概念 4.2 …

OceanBase 助力同方智慧能源,打造安全可靠、高性能的能源数据架构

本文作者&#xff1a;丁泽斌&#xff0c;同方智慧能源数据库工程师 业务背景 作为同方股份有限公司旗下的领军企业&#xff0c;同方智慧能源集团矢志成为全球领先的综合智慧能源解决方案提供商。凭借中核集团和清华大学的科技实力&#xff0c;专注于向建筑、交通、工业、北方供…

一篇文章 学会Qt 样式表(qss)

QML 中风格和主题的设计可以通过配置文件选择现有几种中的一种&#xff0c;或者直接在控件定义时&#xff0c;指定其属性&#xff0c;如背景颜色或者字体大小。在QWidget框架中&#xff0c;则通过了一种叫做qss样式表的东西来进行描述&#xff0c;跟CSS逻辑上类似。 这个qss抽…

Word插件开发

VSTO是Visual Studio Tools for Office的简称&#xff0c;它是Microsoft Visual Studio的一个扩展&#xff0c;用于开发基于Microsoft Office平台的应用程序。VSTO提供了一套API和工具&#xff0c;使开发人员能够利用Visual Studio IDE来开发定制的Office解决方案。 在 Visual…

JAVA Coding 规范

Coding 规范 文章目录 Coding 规范一.文件规范1.1 声明1.2 缩进1.3 空行1.4 空格1.5 对齐1.6 小括号1.7 花括号1.8 代码长度 二.命名规范2.1 命名总则2.2 命名空间2.3 类与接口2.4 方法命名2.5 属性命名2.6 常量命名2.7 变量命名 三.语句规范3.1 语句总则3.2 循环语句3.3 Switc…

快速入门Web开发(中)后端开发(有重点)

你好,我是Qiuner. 为记录自己编程学习过程和帮助别人少走弯路而写博客 这是我的 github gitee 如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 &#x1f604; (^ ~ ^) 想看更多 那就点个关注吧 我会尽力带来有趣的内容 CSDN 图片导入做的不是很好&#xff0c;因此如果有没有…

“python+”潮汐、风驱动循环、风暴潮等海洋水动力模拟

ADCIRC是新一代海洋水动力计算模型&#xff0c;它采用了非结构三角形网格广义波动连续方程的设计&#xff0c;在提高计算精确度的同时还减小了计算时间。被广泛应用于&#xff1a;模拟潮汐和风驱动的循环、预测风暴潮和洪水和近岸海上作业等。 除此之外&#xff0c;ADCIRC也是…

Maven的基础使用

1 Maven概述 引入案例&#xff1a;idea创建javaWeb项目&#xff0c;如何打包并发布tomcat上去&#xff0c;以便于客户端访问&#xff1f; 方案一&#xff1a;DOS命令完成打包&#xff0c;操作性不方便&#xff0c;代码频繁修改&#xff0c;频繁使用命令打包一阶段可以打包&am…

IDEA 中的奇技淫巧

IDEA 中的奇技淫巧 书签 在使用ctrlalt方向键跳转时&#xff0c;或者追踪代码时&#xff0c;经常遇到的情况是层级太多&#xff0c;找不到代码的初始位置&#xff0c;入口。可以通过书签的形式去打上一个标记&#xff0c;后续可以直接跳转到书签位置。 标记书签&#xff1a;c…

面试:Redis(缓存穿透、缓存击穿、缓存雪崩、双写一致、Redis的持久化、Redis的过期策略、Redis的数据淘汰策略、Redis的分布式锁、Redis的集群方案、Redis网络模型)

目录 一、缓存穿透 1、解决方案一&#xff1a; 2、解决方案二&#xff1a; 二、缓存击穿 1、解决方案一&#xff1a; 2、解决方案二&#xff1a; 三、缓存雪崩 1、解决方案一&#xff1a; 2、解决方案二&#xff1a; 3、解决方案三&#xff1a; 4、解决方案四&#…

C#窗体控件ColorDialog

介绍 可以获取颜色&#xff0c;然后对其他控件的颜色属性进行设置。 构造方式 直接把ColorDialog控件拖动到Form窗口。 常用属性 AllowFullOpen 可以控制是否使用自定义颜色&#xff0c;true表示可使用&#xff0c;否&#xff0c;表示不可使用。 AnyColor 如果为tr…

Unity 问题之 开发应用在设备上运行闪屏花屏问题的分析处理

Unity 问题之 开发应用在设备上运行闪屏花屏问题的分析处理 目录 Unity 问题之 开发应用在设备上运行闪屏花屏问题的分析处理 一、简单介绍 二、问题现象 三、问题分析 四、使用空后处理&#xff0c;解决闪屏花屏的显示问题 五、空后处理完整代码 一、简单介绍 Unity 在…

面试:Redis

目录 一、缓存穿透 1、解决方案一&#xff1a; 2、解决方案二&#xff1a; 二、缓存击穿 1、解决方案一&#xff1a; 2、解决方案二&#xff1a; 三、缓存雪崩 1、解决方案一&#xff1a; 2、解决方案二&#xff1a; 3、解决方案三&#xff1a; 4、解决方案四&#x…

【每日算法】理论:深度学习基础 刷题:KMP算法思想

上期文章 【每日算法】理论&#xff1a;常见网络架构 刷题&#xff1a;力扣字符串回顾 文章目录 上期文章一、上期问题二、本期理论问题1、注意力机制2、BatchNorm 和 LayerNorm 的区别3、Bert 的参数量是怎么决定的。4、为什么现在的大语言模型都采用Decoder only架构&#x…

第二篇:Python环境搭建:从初学者到专家

Python环境搭建&#xff1a;从初学者到专家 在编程的世界里&#xff0c;准备好一个高效而舒适的开发环境是走向成功的第一步。在这篇博客文章中&#xff0c;我们将一起探索如何为Python编程搭建一个理想的环境。无论你是完全的新手还是希望提升现有的技能&#xff0c;本文都会…

【Python 对接QQ的接口】简单用接口查询【等级/昵称/头像/Q龄/当天在线时长/下一个等级升级需多少天】

文章日期&#xff1a;2024.04.28 使用工具&#xff1a;Python 类型&#xff1a;QQ接口 文章全程已做去敏处理&#xff01;&#xff01;&#xff01; 【需要做的可联系我】 AES解密处理&#xff08;直接解密即可&#xff09;&#xff08;crypto-js.js 标准算法&#xff09;&…

第10章 项目管理基础知识

一、项目概述 &#xff08;一&#xff09;项目 在既定的项目资源要求和约束下&#xff0c;为实现特定目标而相互联系的一次性活动&#xff08;资源任务&#xff09;。世界上没有两个完全相同的项目项目有资源约束&#xff0c;一定的目的&#xff0c;是一次性。 &#xff08;…