Linux C++ 200行完成线程池类
文章目录
- 1、atomic使用
- 2、volatile关键字
- 3、条件变量
- 4、成员函数指针使用
- 5、线程池
- 6、主线程先退出对子线程影响
- 7、return、exit、pthread_exit区别
- 8、进程和线程的区别
1、atomic使用
原子操作,不可分割的操作,要么完整,要么不完整。
#include <pthread.h>
#include <unistd.h>
#include <iostream>
#include <atomic>
using namespace std;atomic<int> g_acount;
int g_count = 0;
void* ThreadFunc(void* threadData)
{for(int i=0;i<1000000;i++){g_count++;g_acount++;}
}int main(int argc, const char** argv)
{pthread_t pid1,pid2;int err = pthread_create(&pid1,NULL,ThreadFunc,NULL);if(err!=0){cout<<"thread fail---"<<endl;exit(0);}err = pthread_create(&pid2,NULL,ThreadFunc,NULL);if(err!=0){cout<<"thread fail---"<<endl;exit(0);}pthread_join(pid1,NULL);pthread_join(pid2,NULL);cout<<"g_count:"<<g_count<<endl;cout<<"g_acount:"<<g_acount<<endl;return 0;
}
makefile
all: pthreadTextpthreadText:pthreadText.cppg++ -o pthreadText pthreadText.cpp -pthread -std=c++11
运行结果:
2、volatile关键字
用volatile关键字声明的变量,会告诉编译器,这个变量随时可能发生变化,编译器在编译的时候就不会对变量进行激进的优化,每次去读取的时候都会去内存中取,相反,如果编译器进行量优化,可能读取的时候去寄存器去读取这个值,三种特性:易变的、不可优化的、顺序执行的。
3、条件变量
条件本身(while((g_msgQueue.size() == 0) && isRuning == false))是由互斥量保护的,线程在发生改变之前首先锁住互斥量,其他线程不会察觉到这种改变,因为互斥量必须锁住才能计算条件。
#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <pthread.h>
#include <list>
using namespace std;// 初始化
pthread_cond_t g_cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;struct msgStr
{char name[256];int age;int id;
};list<msgStr*> g_msgQueue;
bool isRuning = false;void* outCache(void* data)
{while(true){pthread_mutex_lock(&g_mutex);while((g_msgQueue.size() == 0) && isRuning == false){pthread_cond_wait(&g_cond,&g_mutex);}if(isRuning){pthread_mutex_unlock(&g_mutex);break;}// 消息处理msgStr* jobbuf= g_msgQueue.front();g_msgQueue.pop_front();pthread_mutex_unlock(&g_mutex);// 消息处理cout<<"tid:"<<pthread_self()<<"name:"<<jobbuf->name<<"age:"<<jobbuf->age<<"id:"<<jobbuf->id<<endl;usleep(1000);delete jobbuf;jobbuf = NULL;}
}void inCache(int sig)
{// 收到15这个信号,向消息队列中添加数据if(sig == 15){struct msgStr* msg = NULL;pthread_mutex_lock(&g_mutex);for(int i=0;i<1000;i++){msg = new msgStr();sprintf(msg->name,"name--%d",i);msg->age = i;msg->id = 1000+i;g_msgQueue.push_back(msg);}pthread_mutex_unlock(&g_mutex);pthread_cond_broadcast(&g_cond);}if(sig == 10){isRuning = true;}
}int main()
{// 作为向消息队列中添加数据的函数signal(15,inCache);pthread_t pid1,pid2;pthread_create(&pid1,NULL,outCache,NULL);pthread_create(&pid2,NULL,outCache,NULL);pthread_join(pid1,NULL);pthread_join(pid2,NULL);return 0;
}
4、成员函数指针使用
#include <iostream>
using namespace std;class Test
{
public:Test();~Test(){}void func(int a,int b){cout<<"Test:"<<a<<endl;cout<<"Test:"<<b<<endl;}void func1(int a,int b){cout<<"Test:"<<a<<endl;cout<<"Test:"<<b<<endl;}
};// 函数指针
typedef void (Test::*handler)(int a,int b);const handler handArray[] =
{NULL,NULL,NULL,NULL,NULL,&Test::func1,&Test::func,
};Test::Test()
{(this->*handArray[5])(1,2);
}int main()
{Test t;(t.*handArray[6])(3,5);return 0;
}
makefile
g++ -o main pthreadPoolText.cpp
5、线程池
线程池概率:提前创建多个线程,并通过一个类来统一管理这一堆线程。
工作流程:来了一个任务,从线程池中找一个空闲的线程去处理这个任务,做完任务,循环回来等待新任务,等待新任务,
由pthreadPool.h、pthreadPool.cpp两个文件组成。
1、createPthread函数:创建线程全部线程,并将每个线程结构,放入容器中,函数中的goto语句部分,是为了保证所有线程运行起来,并且都处于pthread_cond_wait未激发状态等待。
2、call函数:中pthread_cond_broadcast唤醒一个或者多个线程,并且记录当前工作线程是否够用,每过十秒钟打印一下信息。
3、stopAll函数:唤醒一个或多个线程,并且释放资源
4、inMsgRecvQueueAndSignal函数:将消息插入消息队列中,并调用call函数。
pthreadPool.h
#ifndef __PTHREADPOOL_H_
#define __PTHREADPOOL_H_
#include <vector>
#include <atomic>
#include <pthread.h>
#include <iostream>
#include <list>
#include <unistd.h>
using namespace std;struct student
{char name[256];unsigned int age;int id;
};class pthreadPool
{
public:pthreadPool();~pthreadPool();public:bool createPthread(int threadNUm = 5);void stopAll();void call();void inMsgRecvQueueAndSignal(char* buf);private:static void* threadFunc(void* threadData);void clearMsgRecvQueue();void msgDispose(char* jobbuf);
private:struct pthreadItem{bool isruning;pthreadPool* _pThis;pthread_t _Handle;pthreadItem(pthreadPool* pthis):_pThis(pthis),isruning(false){}~pthreadItem(){}};
private:static pthread_cond_t m_pthreadCond; // 条件变量static pthread_mutex_t m_pthreadMutex; // 互斥量static bool m_shutdown; // 线程退出标识int m_iThreadNum; // 要创建的线程数time_t m_iLastTime; // 上次线程不够用,时间记录atomic<int> m_iRunThreadNum; // 正在运行线程数量 原子操作vector<pthreadItem*> m_vThread; // 线程容器list<char*> m_msgRecvQueue; // 消息队列int m_iRecvQueueCount; // 收消息队列大小
};#endif // !__PTHREADPOOL_
pthreadPool.cpp文件
#include "pthreadPool.h"pthread_cond_t pthreadPool::m_pthreadCond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t pthreadPool::m_pthreadMutex = PTHREAD_MUTEX_INITIALIZER;
bool pthreadPool::m_shutdown = false;pthreadPool::pthreadPool()
{// 运行的线程数为0,时间为0,消息数0m_iRunThreadNum = 0;m_iLastTime = 0;m_iRecvQueueCount = 0;
}pthreadPool::~pthreadPool()
{clearMsgRecvQueue();
}bool pthreadPool::createPthread(int threadNum)
{if(!threadNum) return false;m_iThreadNum = threadNum;pthreadItem* item;int errCode = 0;for(int i=0;i<threadNum;i++){item = new pthreadItem(this);errCode = pthread_create(&(item->_Handle),NULL,pthreadPool::threadFunc,item);if(errCode!=0){cout<<"线程创建失败:"<<i<<endl;return false;}m_vThread.push_back(item);}vector<pthreadItem*>::iterator iter;
lblfor:// goto语句作用,为了所有线程都正常启动,并且卡在pthread_cond_wait()这for(iter = m_vThread.begin();iter!=m_vThread.end();iter++){if((*iter)->isruning == false){usleep(100*1000); // 单位是微秒,100毫秒goto lblfor;}}return true;
}void* pthreadPool::threadFunc(void* threadData)
{pthreadItem* pThread = (pthreadItem*)threadData;pthreadPool* pPoll = pThread->_pThis;int errCode = 0;pthread_t tid = pthread_self();while(true){// 拿锁errCode = pthread_mutex_lock(&m_pthreadMutex);if(errCode!=0){cout<<"pthread_mutex_lock fail threadFunc errCode"<<errCode<<endl;return (void*)0;}while((pPoll->m_msgRecvQueue.size() == 0) && m_shutdown == false){if(pThread->isruning == false)pThread->isruning = true;// 整个程序初始化的时候,保证所有线程都卡在这里// 当线程走到这里,就会释放锁,处于未激发状态// 一旦被激发,就会去拿锁pthread_cond_wait(&m_pthreadCond,&m_pthreadMutex);}// 判断线程退出条件if(m_shutdown){pthread_mutex_unlock(&m_pthreadMutex);break;}// 走到这里可以去消息处理// 返回第一个元素,没有检查是否存在,走下来就说明有消息char* jobbuf = pPoll->m_msgRecvQueue.front();pPoll->m_msgRecvQueue.pop_front(); // 消息队列数减1--pPoll->m_iRecvQueueCount;// 可以解锁了pthread_mutex_unlock(&m_pthreadMutex);// 能走到这里表示有消息,并且线程正在处理这个消息// 正在工作的线程数加1,原子操作++pPoll->m_iRunThreadNum;// 消息处理//cout<<"消息处理开始:"<<tid<<endl;//sleep(3);//cout<<"消息处理结束:"<<tid<<endl;// 消息处理函数pPoll->msgDispose(jobbuf);// 消息处理结束//释放消息内存,运行线程数--++pPoll->m_iRunThreadNum;}return (void*)0;
}void pthreadPool::msgDispose(char* jobbuf)
{pthread_t tid = pthread_self();struct student* stu = (struct student*)jobbuf;cout<<"tid:"<<tid<<" name:"<<stu->name<<" age:"<<stu->age<<" id:"<<stu->id<<endl;if(stu!=NULL){delete stu;stu = NULL;}
}void pthreadPool::call()
{// 唤醒一个等待该条件的线程,也可能是多个,也就是可以唤醒卡在pthread_cond_waitint errCode = pthread_cond_signal(&m_pthreadCond);if(errCode!=0){cout<<"call fail"<<endl;return;}// 如果工作线程数==开辟线程数需要扩容if(m_iRunThreadNum == m_iThreadNum){time_t currentime = time(NULL);if(currentime-m_iLastTime >10){m_iLastTime = currentime;cout<<"Call()发现线程池中当前空闲线程数量为0,需要考虑扩容"<<endl;}}return;
}void pthreadPool::stopAll()
{if(m_shutdown)return;m_shutdown = true;int errCode = pthread_cond_broadcast(&m_pthreadCond);if(errCode!=0){cout<<"stopAll faile"<<endl;return;}// 等待所有线程结束vector<pthreadItem*>::iterator iter;for(iter=m_vThread.begin();iter!=m_vThread.end();iter++){pthread_join((*iter)->_Handle,NULL);if((*iter))delete *iter;}m_vThread.clear();pthread_cond_destroy(&m_pthreadCond);pthread_mutex_destroy(&m_pthreadMutex);cout<<"成功返回,线程池中线程全部正常退出"<<endl;return;
}void pthreadPool::clearMsgRecvQueue()
{while(!m_msgRecvQueue.empty()){char* buf = m_msgRecvQueue.front();m_msgRecvQueue.pop_front();if(buf!=NULL){delete buf;buf = NULL;}}
}void pthreadPool::inMsgRecvQueueAndSignal(char* buf)
{// 先互斥住int errCode = pthread_mutex_lock(&m_pthreadMutex);if(errCode!=0){cout<<"inMsgRecvQueueAndSignal faile lock"<<endl;}m_msgRecvQueue.push_back(buf);++m_iRecvQueueCount;errCode = pthread_mutex_unlock(&m_pthreadMutex);if(errCode!=0){cout<<"inMsgRecvQueueAndSignal faile unlock"<<endl;}// 激发线程做事call();return;
}
main函数文件测试代码
#include "pthreadPool.h"int main()
{pthreadPool* pool = new pthreadPool();pool->createPthread(6);for(int i=0;i<1000;i++){struct student* stu = new student();sprintf(stu->name,"name-%d",i);stu->age = i;stu->id = 1000+i;pool->inMsgRecvQueueAndSignal((char*)stu);}pool->stopAll();if(pool!=NULL){delete pool;pool = NULL;}pthread_exit(0);
}
makefile
all:pthreadPoolpthreadPool:pthreadPool.h pthreadPool.cpp pthreadPoolText.cppg++ -o pthreadPool pthreadPool.cpp pthreadPoolText.cpp -pthread -std=c++11
6、主线程先退出对子线程影响
观察一下代码:发现主线程退出之后,子线程没有继续打印,也退出了。
造成原因:主线程执行return 之后调用量glibc库里面的exit函数进行清理处理之后,调用系统调用_exit函数进行进程退出,所以并非是主线程退出,导致子线程退出的。
void* func(void* data)
{while(true){cout<<"child loops"<<endl;}return NULL;
}int main()
{ pthread_t pid;int errCode = pthread_create(&pid,NULL,func,NULL);sleep(1);cout<<"main exit"<<endl;return 0;
}
7、return、exit、pthread_exit区别
return返回到调用者
exit 退出当前进程
pthread_exit退出当前线程
8、进程和线程的区别
进程优缺点
进程优点:具有独立的地址空间,隔离性、稳定性比较好,是由操作系统管理,只要系统不出问题,一个进程的错误不会影响到其他进程。
进程缺点:共享资源麻烦
线程优缺点
线程优点:共享进程的资源,创建销毁,切换简单,速度快,占用内存小,CPU利用率高
线程缺点:需要程序员管理,相互影响几率大,一个线程挂掉将导致整个进程挂掉。
总结
一般需要频繁销毁喝创建,要处理大量运算数据,又要很好显示界面及时性比较高的,因为像这些消耗大量CPU,推荐是一个多线程,一般服务器对稳定性比较高的程序推荐使用多进程。
进程之间是如何通信的
可以通过管道pipe,信号量,共享内存,socket套接字,消息队列, 根据信息量大小,进行选择。
线程之间如何通信的
全局变量。或者自定义的消息通信机制,因为线程间共享进程的资源,所以没有像进程中用于数据交换的方式,它通信的主要目的是为了线程同步
多线程同步和互斥有几种方法
线程同步:互斥锁、信号量、信号量
互斥锁:拥有两种状态,lock和unlock,当互斥锁由某个线程持有时,互斥锁状态就变为lock,之后只有该线程有权利打开锁,其他想要获取互斥锁必须都阻塞,直到解锁。
信号量:信号量是一个计数器,用于控制访问有限共享资源数
条件变量:可以让线程满足特定条件才运行,必须搭配互斥锁一起使用。
相关文章:

Linux C++ 200行完成线程池类
文章目录1、atomic使用2、volatile关键字3、条件变量4、成员函数指针使用5、线程池6、主线程先退出对子线程影响7、return、exit、pthread_exit区别8、进程和线程的区别1、atomic使用 原子操作,不可分割的操作,要么完整,要么不完整。 #includ…...

C语言指针剖析(初阶) 最详细!
什么是指针?指针和指针类型野指针指针运算指针和数组二级指针指针数组什么是指针?指针是内存中一个最小单元的编号,也就是地址。1.把内存划分为一个个的内存单元,一个内存单元的大小是一个字节。2.每个字节都给定唯一的编号&#…...

AcWing语法基础课笔记 第三章 C++中的循环结构
第三章 C中的循环结构 学习编程语言语法是次要的,思维是主要的。如何把头脑中的想法变成简洁的代码,至关重要。 ——闫学灿 学习循环语句只需要抓住一点——代码执行顺序! while循环 可以简单理解为循环版的if语句。If语句是判断一次…...
A simple freeD tracking protocol implementation written in golang
可以使用的go版本freed调试代码 可以通过udp发送和接收数据 What is freeD? freeD is a very simple protocol used to exchange camera tracking data. It was originally developed by Vinten and is now supported by a wide range of hard- and software including Unreal…...
简约精美电商小程序【源码好优多】
简介 一款开源的电商系统,包含微信小程序和H5端,为大中小企业提供移动电子商务优秀的解决方案。 后台采用Thinkphp5.1框架开发,执行效率、扩展性、稳定性值得信赖。并且Jshop小程序商城上手难度低,可大量节省定制化开发周期。 功…...

全网详解 .npmrc 配置文件:比如.npmrc的优先级、命令行,如何配置.npmrc以及npm常用命令等
文章目录1. 文章引言2. 简述.npmrc3. 配置.npmrc3.1 .npmrc配置文件的优先级3.2 .npmrc设置的命令行3.3 如何设置.npmrc4. 配置发布组件5. npm常用命令6. 重要备注6.1 yarn6.2 scope命名空间6.3 镜像出错1. 文章引言 今天在某低代码平台开发项目时,看到如下编译配置…...
从0开始学python -31
Python3 模块-1 在前面的几个章节中我们基本上是用 python 解释器来编程,如果你从 Python 解释器退出再进入,那么你定义的所有的方法和变量就都消失了。 为此 Python 提供了一个办法,把这些定义存放在文件中,为一些脚本或者交互…...

Jenkins的使用教程
介绍: Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能。 目的: 最重要目的就是把原来分散在各个机器上繁杂的工作全部…...
1.Maven的坐标和依赖
【maven坐标】1.groupId: 通常与域名反向一一对应2.artifactId: 通常使用实际项目名称3.version: 项目当前版本号4.packaging:maven项目的打包方式,默认是jar5.classifier: 定义构建输出的一些附属构件,例如:nexus-indexer-2.0.0.…...

Jenkins 笔记
Jenkins brew install jenkins-lts brew services restart jenkins-lts brew services stop jenkins-lts b999ff5683464346b6d083f894968121 l 软件构建自动化 :配置完成后,CI系统会依照预先制定的时间表,或者针对某一特定事件,…...

Python和Java语言,哪个更适合做自动化测试?
经常有测试新手问我:Python和Java语言,哪个更适合做自动化测试?本来想简单的回答一下的,但又觉得对不起大家对小编的信任。因此,小编今天专门写了一篇文章来回答这个问题。欢迎各位大佬补充~1、什么是自动化测试&#…...

互联网的路由选择协议
互联网的路由选择协议 文章目录互联网的路由选择协议路由选择协议的几个概念分层次路由选择协议内部网关协议RIP协议距离向量算法RIP协议的报文格式内部网关协议OSPFOSPF的报文格式✨OSPF的特点外部网关协议BGPBGP的报文格式参考本篇主要讨论的是路由表中的路由是如何得出来的。…...
接口幂等性处理
1.Token 机制: a首先客户端请求服务端,获取一个 token,每一次请求都获取到一个全新的 token(当然这个 token 会有一个超时时间),将 token 存入 redis 中,然后将 token 返回给客户端。 b客户端…...

数字孪生智慧机场:透视数字化时代下的航空运营
在《智慧民航建设路线图》文件中,民航局明确指出,智慧机场是实现智慧民航的四个核心抓手之一。这一战略性举措旨在推进数字化技术与航空产业的深度融合,为旅客提供更加智能化、便捷化、安全化的出行服务,进一步提升我国民航发展的…...

SpringBoot 文件上传后查看404的问题和解决404后需要访问两次才能查看的问题
文件上传、图片上传的实现见这个: SpringBootVue 实现头像上传功能_Teln_小凯的博客-CSDN博客 在实现上面的功能后,发现查看图片的时候提示404,解决这个方法如下: 1、配置资源静态文件映射 第一个参数是页面请求的地址&#x…...

定时任务使用总结
定时任务表达式生成工具网站:https://cron.qqe2.com/定时任务选型:xxl-job 官方文档:https://www.xuxueli.com/xxl-job/安装定时任务调度中心 xxl-job-admin第一步、先导入xxl-job的数据库:地址:https://gitee.com/xux…...

Jira和Confluence Server版终止支持倒计时365天,企业应对策略汇总
本文对Atlassian最新的Server版政策进行了解读,并给出应对方案;同时我们也将国内热门的替代工具与jira进行了比较细致的对比,以及介绍替换的优惠政策等。今天是2023年2月15日,距离 Atlassian 旗下 Jira、Confluence 等系列产品中国…...

GEE学习笔记九十一:栅格影像叠置分析
最近发现好多人都在问一个问题,两张影像如何取其相交区域?其实这个问题简单来讲就是多张栅格影像进行叠加分析。在GEE中栅格影像不像矢量数据那样有直接的函数来做数据分析,需要我们自己手动写一些代码来实现这些操作。要实现这个功能有很多方…...

linux系统编程入门
一、搭建环境 1、安装 Linux 系统(虚拟机安装、云服务器) https://releases.ubuntu.com/bionic/ 2、安装 XSHELL、XFTP https://www.netsarang.com/zh/free-for-home-school/ 3、安装 visual studio code https://code.visualstudio.com/ 4、Linu…...
JS代码安全防护常见的方式
文章目录1. 常量的混淆1.1 十六进制字符串1.2 unicode字符串1.3 字符串的ASCII码混淆1.4 字符串常量加密1.5 数值常量加密2. 增加逆向分析难度2.1 数组混淆2.2 数组乱序2.3 花指令2.4 jsfuck3. 代码执行流程的防护3.1 流程平坦化3.2 逗号表达式4. 其他代码防护方案4.1 eval加密…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...

定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...

代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...