并发编程--互斥锁与读写锁
并发编程–互斥锁与读写锁
文章目录
- 并发编程--互斥锁与读写锁
- 1. 基本概念
- 2. 互斥锁
- 2.1 基本逻辑
- 2.2 函数接口
- 2.3示例代码1
- 2.4示例代码2
- 3. 读写锁
- 3.1 基本逻辑
- 3.2示例代码
1. 基本概念
互斥与同步是最基本的逻辑概念:
- 互斥指的是控制两个进度使之互相排斥,不同时运行。
- 同步指的是控制两个进度使之有先有后,次序可控。
2. 互斥锁
2.1 基本逻辑
使得多线程间互斥运行的最简单办法,就是增加一个互斥锁。任何一条线成要开始运行互斥区间的代码,都必须先获取互斥锁,而互斥锁的本质是一个二值信号量,因此当其中一条线程抢先获取了互斥锁之后,其余线程就无法再次获取了,效果相当于给相关的资源加了把锁,直到使用者主动解锁,其余线程方可有机会获取这把锁。
2.2 函数接口
定义
互斥锁是一个特殊的变量,定义如下:
#include <pthread>
pthread_mutex_t m;
一般而言,由于互斥锁需要被多条线程使用,因此一般会将互斥锁定义为全局变量。
初始化与销毁
未经初始化的互斥锁是无法使用的,初始化互斥锁有两种办法:
- 静态初始化
- 动态初始化
静态初始化很简单,就是在定义同时赋予其初值:
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
由于静态初始化互斥锁不涉及动态内存,因此无需显式释放互斥锁资源,互斥锁将会伴随程序一直存在,直到程序退出为止。而所谓动态初始化指的是使用 pthread_mutex_init()
给互斥锁分配动态内存并赋予初始值,因此这种情形下的互斥锁需要在用完之后显式地进行释放资源,接口如下:
#include <pthread.h>// 初始化互斥锁
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);// 销毁互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
接口说明:
- mutex:互斥锁
- attr:互斥锁属性(一般设置为NULL)
加锁与解锁
互斥锁的基本操作就是加锁与解锁,接口如下:
#include <pthread.h>
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;// 加锁
pthread_mutex_lock( &m );// 解锁
pthread_mutex_unlock( &m );
2.3示例代码1
将此前判断偶数的代码用互斥锁加以改进如下:
// concurrency.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <pthread.h>pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;int global = 100;void *isPrime(void *arg)
{while(1){pthread_mutex_lock(&m);// 一段朴素的代码if(global%2 == 0)printf("%d是偶数\n", global);pthread_mutex_unlock(&m);}
}int main()
{pthread_t tid;pthread_create(&tid, NULL, isPrime, NULL);// 一条人畜无害的赋值语句while(1){pthread_mutex_lock(&m);global = rand() % 5000;pthread_mutex_unlock(&m);}
}
运行结果如下:
gec@ubuntu:~$ ./concurrency
492是偶数
2362是偶数
2778是偶数
3926是偶数
540是偶数
3426是偶数
4172是偶数
112是偶数
368是偶数
2576是偶数
1530是偶数
1530是偶数
2862是偶数
4706是偶数
...
gec@ubuntu:~$
可见,有了互斥锁之后,输出的结果正确了。
2.4示例代码2
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>pthread_mutex_t m;void *routine(void *arg)
{char *msg = (char *)arg;#ifdef MUTEXpthread_mutex_lock(&m);#endifwhile(*msg != '\0'){fprintf(stderr,"%c",*msg);usleep(100);msg++;}#ifdef MUTEXpthread_mutex_unlock(&m);#endifpthread_exit(NULL);
}int main(void)
{pthread_mutex_init(&m,NULL);pthread_t t1,t2;pthread_create(&t1,NULL,routine,"AAAAAAAAAAA");pthread_create(&t2,NULL,routine,"BBBBBBBBBBB");pthread_exit(NULL);
}
通过宏定义实现代码的不同运行,输出不同的结果。若不使用互斥锁的话,则直接运行,结果将会是AB交互是输出,两个线程t1,t2会同时运行,交互式输出;若使用互斥锁的话,会输出单独输出一个线程的结果,然后再输出另一个线程的结果。
若要使用互斥锁则如下:
gcc pthread_mutex.c -o pthread_mutex -lpthread -DMUTEX
3. 读写锁
3.1 基本逻辑
对于互斥锁而言,凡是涉及临界资源的访问一律加锁,这在并发读操作的场景下会大量浪费时间。要想提高访问效率,就必须要将对资源的读写操作加以区分:读操作可以多任务并发执行,只有写操作才进行恰当的互斥。这就是读写锁的设计来源。
读写锁提高了资源访问的效率
定义
与互斥锁类似,读写锁也是一种特殊的变量:
pthread_rwlock_t rw;
初始化
与互斥锁类似,读写锁也分成静态初始化和动态初始化:
#include <pthread.h>// 静态初始化:
pthread_rwlock_t rw = PTHREAD_RWLOCK_INITIALIZER;// 动态初始化与销毁:
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
加锁
读写锁最大的特点是对即将要做的读写操作做了区分:
- 读操作可以共享,因此多条线程可以对同一个读写锁加多重读锁
- 写操作天然互斥,因此多条线程只能有一个拥有写锁。(注意写锁与读锁也是互斥的)
#include <pthread.h>// 读锁
// 1,阻塞版本
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
// 2,非阻塞版本
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);// 写锁
// 1,阻塞版本
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
// 2,非阻塞版本
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
操作原则:
- 如果只对数据进行读操作,那么就加 → 读锁。
- 如果要对数据进行写操作,那么就加 → 写锁。
解锁
#include <pthread.h>int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
3.2示例代码
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>char global = 'X';pthread_rwlock_t rwlock;void *routine(void *arg)
{#ifdef RDLOCKpthread_rwlock_rdlock(&rwlock);#elif WRLOCKpthread_rwlock_wrlock(&rwlock);#endifint i = 1000;while(i > 0){fprintf(stderr,"[%c:%c]",*(char*)arg,global);i--;}pthread_rwlock_unlock(&rwlock);pthread_exit(NULL);
}int main(void)
{pthread_rwlock_init(&rwlock,NULL);pthread_t t1,t2,t3;pthread_create(&t1,NULL,routine,"1");pthread_create(&t2,NULL,routine,"2");pthread_create(&t3,NULL,routine,"3");pthread_exit(NULL);
}
相关文章:
并发编程--互斥锁与读写锁
并发编程–互斥锁与读写锁 文章目录 并发编程--互斥锁与读写锁1. 基本概念2. 互斥锁2.1 基本逻辑2.2 函数接口2.3示例代码12.4示例代码2 3. 读写锁3.1 基本逻辑3.2示例代码 1. 基本概念 互斥与同步是最基本的逻辑概念: 互斥指的是控制两个进度使之互相排斥&#x…...
Linux下Docker安装超详细教程(以CentOS为例)
前言 Docker 已成为现代应用开发和部署的标配工具。本教程将手把手教你 在 CentOS 系统上安装 Docker,涵盖从环境准备到验证安装的全流程,并解决常见问题。无论你是运维工程师还是开发者,均可快速上手。 一、环境要求 操作系统 CentOS 7 或更…...
Ubuntu 服务器版本 设置socket服务(Python)
1. 确定 Socket 类型 Socket 服务可以是: 网络 Socket:基于 TCP/UDP 协议(如 Web 服务器、API 服务)。 Unix Domain Socket:本地进程间通信(如 Docker、MySQL 默认使用)。 2. 编写一个简单的 Socket 服务示例(Python) 以 Python 为例,创建一个 TCP Socket 服务:…...
对于GAI虚假信息对舆论观察分析
摘要 生成式人工智能(Generative Artificial Intelligence, GAI)的技术革新重构了信息生产机制,但也加剧了虚假信息对舆论生态的异化风险。 关键词:生成式人工智能、虚假信息、舆论异化、智能治理 一、生成式人工智能虚假信息下…...
HTTP:三.HTTP连接
HTTP(Hypertext Transfer Protocol)是一种用于传输超文本数据的应用层协议。它是互联网上最常用的协议,用于在客户端和服务器之间传输数据。HTTP协议通常用于从Web服务器传输网页和文件到客户端浏览器,并支持其他用途,如传输API数据和传输文件。 HTTP连接是指客户端向服务…...
hyper-v server服务器部署远程访问(我目前环境:hyper-v服务器+路由器+公网ip)
Hyper-v server部署(裸金属方式) 系统镜像下载安装# 下载地址:17763.737.190906-2324.rs5_release_svc_refresh_SERVERHYPERCORE_OEM_x64FRE_zh-cn_1.iso 安装的过程很简单,和安装Windows操作系统没啥区别,这里就不记录了。 安装过程可参考:安装Hyper-v Server 2016 部…...
MCP遇见Web3:从边缘计算到去中心化的无限想象
MCP遇见Web3:从边缘计算到去中心化的无限想象 在数字化转型的浪潮中,边缘计算(MCP,Micro Control Protocol)和Web3技术分别在计算效率与去中心化架构上发挥着各自的优势。当两者融合,会碰撞出哪些火花?作为一名技术极客,我最近开始深度研究MCP与Web3工具的集成,试图探…...
【HarmonyOS Next之旅】DevEco Studio使用指南(十三) -> ArkTS/TS代码重构
目录 1 -> Refactor-Extract代码提取 2 -> Refactor-Convert代码转换 3 -> Refactor-Rename代码重命名 4 -> Move File 5 -> Safe Delete 1 -> Refactor-Extract代码提取 在编辑器中支持将函数内、类方法内等区域代码块或表达式,提取为新方…...
STM32 HAL DHT11驱动程序
DHT11驱动程序会占用TIM3定时器,进行高精度延时。程序共包含4个文件 DHT11.c DHT11.h delay.c delay.h DHT11.c #include "stm32f1xx_hal.h" #include "dht11.h" #include "delay.h" // 添加延时头文件 #define DHT_PORT GPIOB…...
asm汇编源代码之文件操作相关
提供7个子程序: 1. 关闭文件 FCLOSE 2. 打开文件 FOPEN 3. 文件大小 FSIZE 4. 读文件 FREAD 5. 写文件 FWRITE 6. 建立文件 FCREATE 7. 读取或设置文件指针 FPOS 具体功能及参数描述如下 ; ---------------------------- FCLOSE PROC FAR ; IN…...
Github 2025-04-12 Rust开源项目日报Top10
根据Github Trendings的统计,今日(2025-04-12统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10TypeScript项目2uv: 极快的Python软件包安装程序和解析器 创建周期:147 天开发语言:Rust协议类型:Apache License 2.0Star数量:7…...
JAVA学习-练习试用Java实现“实现一个Java程序,对大数据集中的数据进行类型转换”
问题: 实现一个Java程序,对大数据集中的数据进行类型转换。 解答思路: 在Java中,对大数据集中的数据进行类型转换通常意味着将一种数据类型转换为另一种数据类型。以下是一个简单的Java程序示例,它演示了如何对大数据集…...
Android基础彻底解析-APK入口点,xml,组件,脱壳,逆向
第一章:引言与背景 Android逆向工程,作为一种深入分析Android应用程序的技术,主要目的就是通过分析应用的代码、资源和行为来理解其功能、结构和潜在的安全问题。它不仅仅是对应用进行破解或修改,更重要的是帮助开发者、研究人员和安全人员发现并解决安全隐患。 本文主要对…...
Spark RDD算子详解:从入门到精通
一、前言 在大数据处理领域,Apache Spark凭借其高效的内存计算能力,成为了流行的分布式计算框架。RDD(Resilient Distributed Dataset)是Spark的核心概念之一,它是一个分布式的数据集合,提供了丰富的操作接…...
Bootstrap4 卡片
Bootstrap4 卡片 Bootstrap 是一个流行的前端框架,它提供了丰富的组件和工具,使得开发者可以快速构建响应式、美观的网页。其中,Bootstrap4 中的卡片组件(Card)是一个非常实用的功能,可以用来展示图片、文…...
【随行付-注册安全分析报告-无验证方式导致隐患】
前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 1. 暴力破解密码,造成用户信息泄露 2. 短信盗刷的安全问题,影响业务及导致用户投诉 3. 带来经济损失,尤其是后付费客户,风险巨大,造…...
深入解析Antogen意图识别模型:从原理到实践
一、意图识别基础概念 1.1 什么是意图识别 意图识别(Intent Recognition)是自然语言处理(NLP)中的核心任务,旨在确定用户输入背后想要表达的目的或行动请求。它是对话系统理解用户的第一步,直接影响后续的对话管理和响应生成质量。 关键特征ÿ…...
企业年报问答RAG挑战赛冠军方案:从零到SotA,一战封神
RAG挑战赛是什么? 任务是基于企业年报构建问答系统。比赛日的流程简而言之: 解析阶段:获得100份随机企业的年报(PDF格式,每份最多1000页),限时2.5小时完成解析并构建数据库。问答阶段…...
深入理解 HTML5 语义元素:提升网页结构与可访问性
引言 在构建网页的过程中,合理的结构与清晰的语义对于网页的质量、可维护性以及搜索引擎优化(SEO)都至关重要。HTML5 引入了一系列语义元素,为开发者提供了更精准描述网页内容的工具。本文将深入探讨 HTML5 语义元素的作用、使用…...
DeepSeek vs Grok vs ChatGPT:三大AI工具优缺点深度解析
一、DeepSeek:低成本与中文专精的本地化AI 优点 中文处理能力卓越 DeepSeek针对中文语法和文化背景进行了深度优化,尤其在古文翻译、诗歌创作和技术文档生成中表现突出,远超ChatGPT的中文支持能力。高效推理与低成本 采用混合专家ÿ…...
MCP(模型上下文协议)简单案例
MCP(模型上下文协议)的标准化接口 MCP(Model Context Protocol,模型上下文协议)是由Anthropic开发的一种开放协议,旨在标准化大型语言模型(LLM)与外部数据源和工具的交互方式1。它就…...
BGP基础概念与核心架构
一、BGP 协议定义与定位 BGP(Border Gateway Protocol,边界网关协议) 是互联网中唯一的域间路由协议(EGP),用于在不同自治系统(AS)之间交换路由信息。与 OSPF、IS-IS 等域内路由协议…...
【经济保护主义叙事】
第一层:表面逻辑——经济保护主义叙事 公开理由:特朗普宣称加征关税是为了“保护美国制造业”“减少贸易逆差”“维护国家安全”,并强调通过关税迫使贸易伙伴降低壁垒,促进产业链回流美国。 底层逻辑:通过民族主义叙事…...
遵循IEC 62304:构建安全可靠的医疗器械软件
目录 一、IEC 62304 标准概述 1. 标准定位与适用范围 二、核心内容与要求 1. 软件安全等级(Software Safety Classification) (1)分级标准 (2)分级依据 (3)验证要求 2. 软件…...
ARM 架构下 spin_lock 实现
阅读该文章前,需要对原子指令有所了解,推荐阅读 聊一聊原子操作和弱内存序 1、概念 内核当发生访问资源冲突的时候,可以有两种锁的解决方案选择: 一个是原地等待一个是挂起当前进程,调度其他进程执行(睡眠…...
计算机视觉算法实现——SAM实例分割:原理、实现与应用全景
✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连✨ 1. 实例分割领域概述 实例分割(Instance Segmentation)是计算机视觉领域最具挑战性的任务之一,…...
asm汇编字符串操作
提供8个子程序: 1. 字符串长度 STRLEN 2. 字符串替换 REPLACE 3. 字节查找 SEARCHB 4. 双字节查找 SEARCHW 5. 输入字符串 GETSTR 6. 输出字符串 OUTSTR 7. 复制字符串 COPYSTR 8. 查找字符串 SRCHSTR 具体功能及参数描述如下 STRLEN PROC FAR ; IN: ; DS:DXSTRING ;…...
[MSPM0开发]之三MSPM0G3507之时钟系统
一、MSPM0G3507时钟模块概述 MSPM0G3507的时钟系统隶属于其电源管理和时钟单元 (PMCU) 。 PMCU主要负责电源管理、时钟配置和复位控制功能。 时钟模块包含内部和外部 振荡器 oscillators、 时钟监测器 clock monitors以及时钟选择和控制逻辑。 提供了频率时钟计数器frequency …...
如何绕过WAF实现SQL注入攻击?
引言 在渗透测试中,SQL注入(SQLi)始终是Web安全的核心漏洞之一。然而,随着企业广泛部署Web应用防火墙(WAF),传统的注入攻击往往会被拦截。本文将分享一种绕过WAF检测的SQL注入技巧…...
基础数学:图论与信息论
微积分与概率论由此进:基础数学:微积分和概率与统计-CSDN博客 线代与优化理论由此进:基础数学:线性代数与优化理论-CSDN博客 数值分析与离散数学由此进:基础数学:数值分析与离散数学-CSDN博客 四、图论与…...
05-RabbitMQ 面试题-mk
1.RabbitMQ-如何保证消息不丢失? 消息中间件的好处 提供了系统之间的异步调用,让服务与服务之间解耦削峰、填谷场景: 异步发送(验证码、短信、邮件…)MySQL和Redis , ES之间的数据同步分布式事务削峰填谷 消息发送者(publisher )把消息发送给交换机(exchange),由交…...
当当平台商品详情接口设计与调用指南
当当平台商品详情接口设计与调用指南 接口名称 GET /api/product/detail 图书商品核心信息查询接口 请求参数说明 参数名称 类型 是否必填 说明 isbn string 是 国际标准书号(支持13位/10位) product_id string 否 平台内部商品编号(与…...
基于vue框架的住院信息管理系统k08hv(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
系统程序文件列表 项目功能:患者,工作人员,预约住院,住院登记,科室信息,床位信息,床位分配,转病房,病历信息,费用信息,出院登记 开题报告内容 基于Vue框架的住院信息管理系统开题报告 一、选题背景与意义 (一)选题背景 随着医疗技术的不…...
Flink 编程基础:Scala 版 DataStream API 入门
大家好!我是心海 流处理技术在大数据时代正变得越来越重要,而 Apache Flink 作为领先的流处理引擎,凭借其高性能、低延迟和丰富的 API 受到了广泛关注。本文将以 Scala 语言为例,详细讲解 Flink DataStream API 的基本编程模型&am…...
noscript 标签是干什么的
vue public目录下的 index.html 会有 <noscript> 标签不知道是干吗的。 其实 noscript 标签在不支持或是禁用JavaScript 的浏览器中显示替代的内容。这个元素可以包含任何 HTML 元素。这个标签的用法也非常简单: <noscript><strong>Were sorry …...
基于 Streamlit 的 PDF 编辑器
你想实现一个基于 Streamlit 的 PDF 编辑器,功能包括: PDF 转 WordPDF 表格提取PDF 拆分页面PDF 转图片 下面是一个初步的 Streamlit 应用框架代码示例,集成了以上功能。使用了常见库如 pdfplumber、PyMuPDF(fitz)、…...
Oracle 排除交集数据 MINUS
MINUS 是 Oracle 数据库中的一种集合操作符,用于返回第一个查询结果中存在但第二个查询结果中 不存在 的 唯一行。其核心功能是 排除交集数据,常用于数据差异分析或过滤特定记录 一、核心功能 排除交集:返回第一个查询结果中 不在第二个查询结…...
.net Core 和 .net freamwork 调用 deepseek api 使用流输出文本(对话补全)
.net Core 调用 deepseek api 使用流输出文本 简下面直接上代码(.net core):最后再贴一个 .net Freamwork 4 可以用的代码TLS 的代码至关重要的:(下面这个) 简 在官网里面有许多的案例:我们通过…...
巧用递归算法:破解编程难题的“秘密武器”
专栏:算法的魔法世界 个人主页:手握风云 目录 一、递归 二、例题讲解 2.1. 汉诺塔问题 2.2. 合并两个有序链表 2.3. 反转链表 2.4. 两两交换链表中的节点 2.5. Pow(x, n) 三、总结 一、递归 递归的概念 一个方法在执行过程中调用自身, 就称为递…...
C++、Python的输入输出及相关的处理操作
一、C 输入输出及相关处理操作 C 是算法竞赛中常用的语言,因其高效性适合处理大数据或严格时间限制的题目。输入输出是基础,但细节处理(如速度优化、格式要求)对比赛结果影响很大。 1. 基本输入输出 C 使用 <iostream> 库…...
Unity协程从入门到精通:告别卡顿,用Coroutine优雅处理异步与时序任务 (Day 27)
Langchain系列文章目录 01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南 02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖 03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南 04-玩转 LangChai…...
Web攻防—SSRF服务端请求伪造Gopher伪协议无回显利用
前言 重学Top10的第二篇,希望各位大佬不要见笑。 SSRF原理 SSRF又叫服务端请求伪造,是一种由服务端发起的恶意请求,SSRF发生在应用程序允许攻击者诱使服务器向任意域或资源发送未经授权的请求时。服务器充当代理,执行攻击者构造…...
青少年编程与数学 02-016 Python数据结构与算法 16课题、贪心算法
青少年编程与数学 02-016 Python数据结构与算法 16课题、贪心算法 一、贪心算法的基本概念定义组成部分 二、贪心算法的工作原理三、贪心算法的优点四、贪心算法的缺点五、贪心算法的应用实例(一)找零问题(二)活动安排问题&#x…...
Linux系统Debian最新版12.10安装详细教程,虚拟机系统安装(附系统镜像资源)
前言 Debian是一款以稳定性著称的免费开源Linux发行版,支持多种硬件架构,遵循自由软件原则,并提供庞大的软件仓库和长期维护。 一、环境下载 Debian 12.10镜像下载 💾资源下载👉Debian 12.10镜像下载:ht…...
android display 笔记(十一)surfaceflinger 如何将图层传到lcd驱动的呢?
SurfaceFlinger->>HWC: 提交所有图层(Layer) HWC->>DRM/KMS: 硬件合成(Overlay)或 GPU 合成 DRM/KMS->>LCD Driver: 配置显示控制器(CRTC/Encoder) LCD Driver->>Display: 通过 MI…...
管理大规模监控技术栈的最佳实践
集中管理可观测性数据 集中化监控数据有助于打破信息孤岛,提供系统全景视图。彭博社发现,当团队各自为战时,系统中断往往持续很久才有人意识到多个团队正在独立处理同一问题。通过数据集中管理,他们获得了更全面的基础设施视图&a…...
深入探索C++ STL:从基础到进阶
目录 引言 一、什么是STL 二、STL的版本 三、STL的六大组件 容器(Container) 算法(Algorithm) 迭代器(Iterator) 仿函数(Functor) 空间配置器(Allocator…...
GitHub Desktop 推送报错 Authentication Failed 身份验证失败
弹窗问题: Authentication Failed 验证失败 We were unable to authenticate with https://gitee.com/.Pleaseenter your username and password to try again. 用户名 密码 Depending on your repository’s hosting service, you might need touse a Personal Acc…...
Webpack中的文件指纹:给资源戴上个“名牌”
Webpack中的文件指纹:给资源戴上个“名牌” 你是否想过,当你修改代码后,浏览器为什么仍然拿着旧版资源不放?秘密就在于——文件指纹!简单来说,文件指纹就像给每个构建出来的文件贴上独一无二的“姓名牌”&…...
ESP32开发之ubuntu环境搭建
1. 在Ubuntu官网下载Ubuntu server 20.04版本https://releases.ubuntu.com/20.04.6/ 2. 在vmware下安装Ubuntu 3. 改Ubuntu静态IP $ sudo vi /etc/netplan/00-installer-config.yaml# This is the network config written by ‘subiquity’ network: renderer: networkd eth…...