Linux内核视角:线程同步与互斥的原理、实现与锁优化策略
Linux系列
文章目录
- Linux系列
- 前言
- 一、前提知识
- 二、线程互斥概念引入
- 三、线程互斥
- 3.1 什么是线程的互斥
- 3.2 线程互斥的实现
- 四、锁的实现原理
前言
在前两篇文章中,我们已经对线程的相关概念及基本操作进行了深入介绍。在本篇中,我们将深入探讨编写多线程程序时非常重要的一个环节——保证线程的同步互斥,以此确保程序执行结果的正确性。
一、前提知识
在正式介绍之前,我们需要对前面提到的知识进行简单回顾:
以下概念,在我们介绍示例时,会再次点出
- 临界资源:多线程执行流共享的资源就叫做临界资源
- 临界区:每个线程内部,访问临界资源的代码,就叫做临界区
- 原子性:不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完成(后面会结合实现分析)。
- 并发:多个任务在重叠的时间内交替执行(线程切换),但不一定是同时的。
- 竞态条件:是并发编程和多线程编程环境中因执行顺序的不确定性导致程序结果不可预测的一种缺陷。当多个执行流未经合理同步的访问同一共享资源时,其执行结果可能因调度顺序不同而产生影响(后面结合示例分析)。
二、线程互斥概念引入
接下来,我们将通过编写程序模拟"黄牛"抢购演唱会门票的场景。这个程序不仅生动还原了多线程环境下的竞争状态,更直观展现出线程同步互斥问题的核心矛盾与解决需求,请认真分析:
#include<iostream>
#include<unistd.h>
#include<pthread.h>
#include<vector>
#include<stdio.h>
#include<cstring>using namespace std;
#define NUM 3int ticket =100;//模拟100张票class threadDate{//用来记录线程名,方便对结果观察
public:threadDate(int num){str="thread "+to_string(num);}string str;
};
void* getTicket(void*args)//用来模拟抢票
{threadDate*ptr=(threadDate*)args;const char*name=(const char*)ptr->str.c_str();while(true){if(ticket>0)//票数为0,停止抢购{usleep(50000);printf("who=%s, get a ticket: %d\n", name, ticket);//抢到后输出票号及个人信息ticket--;//票数减一}else break;}return nullptr;
}
int main()
{vector<pthread_t> vtid;vector<threadDate*> thread_date;for(int i=0;i<NUM;i++)//创建三个线程(黄牛){threadDate*tdate=new threadDate(i);thread_date.push_back(tdate);pthread_t tid;pthread_create(&tid,nullptr,getTicket,thread_date[i]);vtid.push_back(tid);}for(auto x:vtid)//等待线程{pthread_join(x,nullptr);}for(auto x:thread_date)//释放申请空间{delete x;}cout<<"main quit...."<<endl;return 0;
}
观察程序的抢票结果会发现一个异常现象:当剩余票数已经为 0
时,程序并未按照预期停止抢票,甚至出现了 -1
这样不合理的票号。这种情况的根源在于多线程的并发特性:由于线程的执行顺序具有不确定性,当某个线程执行到 if
条件判断并确认剩余票数大于 0
后,在执行后续票数扣减操作前,CPU 调度可能将其暂停转而执行其他线程。
假设此时剩余票数为 1
,新调度的线程通过 if
条件检查后进入抢票逻辑,将票数减为 0
并完成操作。当先前被暂停的线程重新获得调度权时,它会从暂停处继续执行,由于其在被暂停前已经通过 if
条件判断,因此会继续执行扣减操作,最终导致抢到的票号变为 0
甚至出现负数,所以违背了我们的预期。
这里补充一个关键知识点:上述异常结果频发,与usleep
系统调用密切相关。在Linux系统中,当进程因usleep
调用从用户态切换至内核态,再返回用户态时,操作系统会触发时间片检查机制。一旦当前执行流的时间片耗尽,操作系统便会立即执行上下文切换,将CPU资源分配给其他就绪线程。这一机制使得多线程并发执行时,线程执行顺序充满不确定性,极大增加了数据竞争与执行逻辑混乱的风险,从而导致如抢票程序中出现的异常情况,其根本原因就是因为该操作不是原子的。
上述程序还存在一个不易触发的潜在问题,即在类似执行逻辑中对共享资源进行--/++
操作,都可能导致程序执行逻辑错误,接下来我们将结合硬件进行详细分析:
这里以 --
操作为例进行详细说明。当程序被编译成可执行程序时,--
语句会被转化为三条汇编指令,这意味着它在 CPU 中需要分三个步骤来执行。例如对于 ticket--
操作,其对应的汇编指令依次为:1. mov [xxx] eax
,该指令的作用是将内存中存储 ticket
值的位置内容读取并拷贝到寄存器 eax
中;2. --
,这一步在寄存器 eax
中对数值执行减一操作;3. mov eax [xxx]
,此指令负责将寄存器 eax
中经过减一处理后的结果重新写回到内存中存储 ticket
的位置。通过这样的过程,完成了对共享资源 ticket
的减一操作,而在这个过程在,执行流又随时都又被切换的可能,这就造成了下面的问题:
从上面的执行逻辑很容易看出,程序的执行,存在潜在风险。
为解决上述两个核心问题,必须确保线程在访问临界资源时满足严格的互斥条件。只有当线程对临界资源的访问具备互斥性,才能避免因并发访问导致的数据竞争与逻辑错误,从而保证多线程程序执行结果的准确性与可靠性。
三、线程互斥
3.1 什么是线程的互斥
线程互斥是一种重要的并发控制机制,其核心在于同一时刻仅允许单个线程访问共享资源。借助互斥机制,当某个线程正在对共享内存进行读写操作时,其他线程会被暂时阻止访问同一资源。这一约束能够有效规避竞态条件——即多个线程同时访问和修改共享数据可能引发的不可预测行为,从而确保数据的一致性和程序执行的正确性。
3.2 线程互斥的实现
在多线程编程的场景中,为了实现线程互斥,通常会采用互斥量这一重要工具。具体而言,我们会在每个线程进入临界区(也就是访问共享资源的代码段)的入口处,借助互斥量来设置锁机制。当一个线程尝试进入临界区时,它会尝试获取互斥量对应的锁。一旦某个线程成功获取到锁,就意味着它获得了在该时刻访问共享资源的唯一权限。此时,其他试图进入同一临界区的线程会被阻塞,它们必须等待持有锁的线程完成对共享资源的访问,并释放互斥量对应的锁之后,才有机会去竞争获取锁并进入临界区。通过这种方式,互斥量确保了共享资源在同一时刻只能被一个线程安全访问,有效避免了因多线程并发访问共享资源而引发的数据竞争和不一致问题。
互斥量本质上是一个特殊的变量,它有两种状态:锁定(Locked)和解锁(Unlocked)。在多线程环境中,当多个线程需要访问同一共享资源时,互斥量就可以发挥作用,保证在同一时刻只有一个线程能够访问该资源,从而避免数据竞争和不一致问题。
接口函数
动态初始化互斥量:
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
功能:用于动态初始化互斥量
参数
- mutex:这是一个指向 pthread_mutex_t 类型的指针,该指针指向需要被初始化的互斥量对象。pthread_mutex_t 是互斥量的数据类型,代表一个互斥锁。
- attr:这是一个指向 pthread_mutexattr_t 类型的指针,该指针指向互斥量的属性对象。如果将其设置为 NULL,则表示使用默认的互斥量属性。
- restrict 是 C 语言里的一个类型限定符,它的作用是向编译器提供一种优化提示,告知编译器某个指针是访问其所指向对象的唯一途径
在使用该函数之前首先要在定义一个互斥量,动态初始化的形式可以指定互斥的的属性。
静态初始化互斥量:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
使用 PTHREAD_MUTEX_INITIALIZER
宏,这是一种简单直接的方式,适用于不需要自定义属性,使用默认属性的情况。这个宏它会将互斥量初始化为默认属性状态。在编译时,这个宏会被替换成一个合适的初始值,使得互斥量处于可用的初始状态。
加锁
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
功能:用于对互斥量进行加锁操作,以实现线程间对共享资源的互斥访问。
参数
- mutex:这是一个指向 pthread_mutex_t 类型的指针,代表要进行加锁操作的互斥量对象。在调用此函数之前,该互斥量必须已经被正确初始化
返回值
- 成功:若函数调用成功,会返回 0,表示当前线程已经成功获取到互斥量的锁,此时该线程可以访问被该互斥量保护的共享资源。若互斥量已经被其他线程锁定,当前线程会被阻塞,进入等待状态。(阻塞是正常现象,并不代表函数执行失败)
- 失败:若函数调用失败,会返回一错误码。
解锁
#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);
功能:用于释放互斥量锁
参数
- mutex:这是一个指向 pthread_mutex_t 类型的指针,代表要进行解锁操作的互斥量对象。在调用此函数之前,该互斥量必须已经被正确初始化,并且当前线程必须已经成功获取到该互斥量的锁。
返回值
- 成功:若函数调用成功,会返回 0,表示当前线程已经成功释放了互斥量的锁,此时其他等待该锁的线程有机会获取锁并进入临界区。
- 失败:若函数调用失败,会返回错误码。
销毁锁
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
功能:用于销毁已经初始化的互斥量
参数
-mutex:这是一个指向 pthread_mutex_t 类型的指针,代表要进行销毁操作的互斥量对象。在调用此函数之前,该互斥量必须已经被正确初始化。
返回值
- 成功:若函数调用成功,会返回 0,表示互斥量已被成功销毁,与之关联的系统资源已被释放。
- 失败:若函数调用失败,会返回错误码。
注意:在销毁互斥量之前,必须确保没有任何线程正在持有该互斥量的锁,也没有线程正在等待获取该互斥量的锁。否则会发生未定义错误。
下面我们就使用互斥量对我们的抢票程序进行加锁:
#define NUM 3int ticket =100;
pthread_mutex_t mutex;//定义一个互斥量class threadDate{//用来记录线程名,方便对结果观察
public:threadDate(int num){str="thread "+to_string(num);}string str;
};
void* getTicket(void*args)//用来模拟抢票
{threadDate*ptr=(threadDate*)args;const char*name=(const char*)ptr->str.c_str();while(true){pthread_mutex_lock(&mutex);//对互斥量加锁(申请互斥锁)if(ticket>0){usleep(50000);printf("who=%s, get a ticket: %d\n", name, ticket);//抢到后输出票号及个人信息ticket--;//票数减一pthread_mutex_unlock(&mutex);//对互斥量解锁}else{pthread_mutex_unlock(&mutex);//对互斥量解锁break;}}return nullptr;
}int main()
{vector<pthread_t> vtid;vector<threadDate*> thread_date;pthread_mutex_init(&mutex,nullptr);//初始化互斥量,属性默认for(int i=0;i<NUM;i++)//创建三个线程(黄牛){threadDate*tdate=new threadDate(i);thread_date.push_back(tdate);pthread_t tid;pthread_create(&tid,nullptr,getTicket,thread_date[i]);vtid.push_back(tid);}for(auto x:vtid)//等待线程{pthread_join(x,nullptr);}for(auto x:thread_date)//释放申请空间{delete x;}cout<<"main quit...."<<endl;return 0;
}
从程序的执行结果能够清晰地看到,竞态条件已然得到妥善解决。然而,令人疑惑的是,所有的票似乎都被一号线程抢购一空,这背后究竟隐藏着怎样的缘由呢?
这种现象,就是因为锁分配不够合理,导致其他线程得不到执行,我们称这种现象为线程的饥饿,具体原因也很好解释:在多线程的抢票场景中,当线程 1 成功申请到锁后,便获得了进入临界区的权限。此时,其他线程由于锁已被占用,会被无情地阻塞(挂起),只能无奈地处于等待状态。
当线程 1 完成了抢票逻辑,并释放了手中的锁,它会毫不犹豫地再次进入循环,发起锁的申请。而那些被阻塞的线程,要想重新参与竞争,首先得经历一个状态转变的过程——从阻塞恢复为运行状态。这个转变并非一蹴而就,需要耗费较长的时间,这也就造成了线程对锁的竞争能力不同。
就在其他线程还在努力完成状态转变,尚未能开展申请锁的操作时,反应迅速的线程 1 很可能已经再次成功获取到锁,从而又一次堂而皇之地进入临界区。如此循环往复,其他线程始终难以获得进入临界区的机会,进而导致了饥饿问题的出现,使得它们在抢票过程中几乎毫无收获。
要解决这个问题我们就需要让所有的线程获取锁,是按照一定顺序的,可以通过让抢票执行后休眠一段时间,让其他线程有足够的时间完成由阻塞状态转为运行状态,从而公平的竞争锁。按照一定的顺序性获取资源-----同步!!!
void* getTicket(void*args)//用来模拟抢票
{threadDate*ptr=(threadDate*)args;const char*name=(const char*)ptr->str.c_str();while(true){pthread_mutex_lock(&mutex);//对互斥量加锁(申请互斥锁)if(ticket>0){usleep(50000);printf("who=%s, get a ticket: %d\n", name, ticket);//抢到后输出票号及个人信息ticket--;//票数减一pthread_mutex_unlock(&mutex);//对互斥量解锁}else{pthread_mutex_unlock(&mutex);//对互斥量解锁break;}usleep(100);//执行完抢票逻辑,休眠100微秒}return nullptr;
}
从程序的执行结果能够清晰地看到,线程饥饿已然得到了解决。
锁本身就是共享资源,而锁又需要保护临界资源,这也就要求锁的申请和释放必须是原子性的,这是如何做到的呢?下面我们就来感性的理解一下锁定实现原理。
四、锁的实现原理
我们常常提及的原子性这一概念,其实可以进行一个相对通俗的理解:通常而言,一条汇编语句所代表的操作具备原子性。也就是说,该操作在执行过程中是不可分割的,要么完整地执行完毕,要么完全不执行,不会出现执行到一半被中断的情况。
我们申请锁的代码,会被编译为:
当然该执行逻辑在执行过程中依然有被切换的情况,但由于线程上下文数据的保存机制,所以该逻辑即使被切换依然是原子性的,大家结合我之前的分析逻辑,自行分析吧,这里就不演示了。
在执行解锁操作时,直接修改互斥量即可,这种方式可以让我们使用其他的线程对该锁进行释放,让我们编写的多线程代码更具灵活性。
本篇就介绍到这里了,在使用线程锁时,依然会面临许多问题,这些问题我就方在下篇介绍了,希望本篇对你有所帮助。
相关文章:
Linux内核视角:线程同步与互斥的原理、实现与锁优化策略
Linux系列 文章目录 Linux系列前言一、前提知识二、线程互斥概念引入三、线程互斥3.1 什么是线程的互斥3.2 线程互斥的实现 四、锁的实现原理 前言 在前两篇文章中,我们已经对线程的相关概念及基本操作进行了深入介绍。在本篇中,我们将深入探讨编写多线…...
【区块链】Uniswap详细介绍
一、前言 本文将结合网上的资料和博主的理解,像大家详细介绍Uniswap,包括其核心概念、工作原理、版本演进、代币经济学以及风险点,适合想深入了解去中心化交易所(DEX)机制的用户。 二、Uniswap是什么 Uniswap 是一个…...
YOLOv8的Python基础--函数篇
1. 文件/目录操作相关函数 这些函数来自 os 和 shutil 模块: 函数/用法作用示例说明os.listdir(dir)列出目录下所有文件名os.listdir("./images")返回文件名列表(不包含路径)os.path.join()拼接路径os.path.join("dir"…...
vue源代码采用的设计模式分解
No.大剑师精品GIS教程推荐0地图渲染基础- 【WebGL 教程】 - 【Canvas 教程】 - 【SVG 教程】 1Openlayers 【入门教程】 - 【源代码示例 300】 2Leaflet 【入门教程】 - 【源代码图文示例 150】 3MapboxGL【入门教程】 - 【源代码图文示例150】 4Cesium 【入门教程】…...
强化学习是AI Agent的进化引擎还是技术枷锁呢?
第一章 强化学习:Agent的“灵魂”觉醒之路 1.1 AlphaGo的启示:从规则到目标驱动的范式革命 2016年AlphaGo击败李世石的事件,标志着RL首次在复杂决策场景中展现其颠覆性价值。通过深度神经网络与RL的结合,AlphaGo无需依赖人类棋谱…...
python简易实现勒索病毒
python简易实现勒索病毒 1.首先介绍Crypto库1.1首先是对称加密1.2 非对称加密1.3 哈希 2.生成RSA密钥并保存3.文件加密4.文件解密1. 导入必要的模块2. 定义解密函数3. 设置私钥的密码4. 打开并读取文件5. 导入私钥6. 读取加密数据7. 解密会话密钥8. 创建 AES 解密器9. 解密数据…...
Nacos源码—4.Nacos集群高可用分析三
大纲 6.CAP原则与Raft协议 7.Nacos实现的Raft协议是如何写入数据的 8.Nacos实现的Raft协议是如何选举Leader节点的 9.Nacos实现的Raft协议是如何同步数据的 10.Nacos如何实现Raft协议的简版总结 6.CAP原则与Raft协议 (1)CAP分别指的是什么 (2)什么是分区以及容错 (3)为…...
AWS WebRTC如何实现拉流?内部是这样实现的
当我们通过手机上的app选择某一个Iot设备,例如,摄像头,想看实时视频的时候,aws都做了什么?最近在搞自研Iot项目,借机整理一下相关流程。 App通过 AWS SDK 发起拉流请求的内部机制是AWS Kinesis Video Streams (KVS) WebRTC 模式中一个非常关键的问题。 一、KVS WebRTC …...
NGINX `ngx_http_browser_module` 深度解析与实战
1. 模块定位 ngx_http_browser_module 在 HTTP 头 User-Agent 解析的基础上,给出三个内置变量: 变量作用典型值$modern_browser当 UA 被判定为 现代浏览器 时取 modern_browser_value 指定的值;否则为空modern. / 1$ancient_browser当 UA 被…...
Elasticsearch知识汇总之 ElasticSearch高可用方案
六 ElasticSearch高可用方案 6.1 高可用架构 请求协调节点根据负载均衡,转发给主分片节点,主分片同步复制给从节点,主从节点都写入完成返回客户端请求成功。对于读请求,协调负载到任意节点数据节点,数据节点把各自符合…...
多线程2-多线程编程
引入 当我们想要代码能够实现并发执行时,我们可以使用多进程进行并发编程(在Java中并不推荐这种方式,许多API在Java标准库中都没有提供),也可以使用多线程进行并发编程(系统提供了相关的API,Ja…...
电商系统中单商户和多商户的区别
在电商的商业版图上,单商户与多商户模式如同两条并行的发展脉络,各自构建起独特的商业生态。它们在运营逻辑、商业模式等多方面存在显著差异,这些差异不仅塑造了不同的平台特性,也深刻影响着企业的发展路径。接下来,我…...
【东枫科技】代理英伟达产品:智能网卡的连接线
文章目录 总览详细:NVIDIA 400Gb/s QSFP-DD 线缆详细:NVIDIA 400Gb/s OSFP 线缆详细:NVIDIA 200Gb/s QSFP56 线缆详细:NVIDIA 100Gb/s QSFP28 线缆 总览 详细:NVIDIA 400Gb/s QSFP-DD 线缆 详细:NVIDIA 400…...
使用ip池后,爬虫还被封,是什么原因呢?
嘿,亲爱的小伙伴们!今天我们聊一个让很多爬虫工程师抓狂的问题:明明用上了IP池,结果爬虫还是被封了!怎么回事呢?如果你也曾在爬虫与反爬的“猫鼠游戏”里痛苦“翻车”,别着急,这篇文…...
C++23 新利器:深入解析栈踪迹库 (P0881R7)
文章目录 为何需要标准化的栈踪迹?P0881R7 的核心组件与使用基本用法示例与异常处理的集成优势与价值潜在的考量总结 对于 C 开发者而言,调试和错误诊断一直是开发周期中不可或缺但又充满挑战的一环。当程序崩溃或发生未预期行为时,获取清晰、…...
2025-05-06 事业-独立开发项目-记录
摘要: 2025-05-06 事业-独立开发项目-记录 独立开发项目记录 Product Hunt | InDev 独立开发者导航站https://www.producthunt.com/ Nomads.com - Best Places to Live for Digital Nomads (formerly Nomad List)https://nomads.com/ InDev 独立开发者导航站https://indev.bei…...
【Linux系统】探索进程等待与程序替换的奥秘
文章目录 前言一、重谈进程创建1.1 fork 函数1.2 写时拷贝1.3 fork 的常规用法1.4 fork 调用失败的原因1.5 创建一批进程 二、进程终止2.1 进程退出场景2.2 strerror 函数的作用2.3 errno 全局变量2.4 程序异常机制2.5 进程退出方式 三、进程等待3.1 进程等待必要性3.2 进程等待…...
Github 2025-05-06Python开源项目日报 Top10
根据Github Trendings的统计,今日(2025-05-06统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目10C++项目2TypeScript项目1系统设计指南 创建周期:2507 天开发语言:Python协议类型:OtherStar数量:241693 个Fork数量:42010 次…...
【愚公系列】《Manus极简入门》021-音乐创作助手:“音符魔术师”
🌟【技术大咖愚公搬代码:全栈专家的成长之路,你关注的宝藏博主在这里!】🌟 📣开发者圈持续输出高质量干货的"愚公精神"践行者——全网百万开发者都在追更的顶级技术博主! …...
【Azure Redis】Redis导入备份文件(RDB)失败的原因
问题描述 在测试Azure Redis的导入/导出备份文件的功能中,突然发现在Redis 4.0上导入的时候,一直报错。 image.png 问题解答 因为门户上只是显示导入失败,没有任何错误消息说明。根据常理推断,Redis 的RDB文件格式都具有一致性。居…...
git “分离头指针”(detached HEAD) 状态。
在 Git 中,当你运行 git branch 命令时,看到如下输出: * (detached from 5b596b5)master 其中的: * (detached from 5b596b5) 表示你当前处于 “分离头指针”(detached HEAD) 状态。 🧠 什…...
Gitee的介绍
目录 1.Gitee介绍: 1.1 代码托管 1.2 本土化优势 1.3 企业级服务 1.4 开源生态 1.5 多形态适配 定位:国内开发者首选的高效代码协作平台,兼顾个人开源与企业级私有开发需求。 2.Gitee和GitHub区别 3.Gitee使用教程 4.Gitee相关…...
NoUniqueKey问题和Regular join介绍
问题背景 在flink任务中,遇到了 NoUniqueKey Join的情况,导致了数据膨胀,和下游结果与数据库数据不一致问题 那NoUniqueKey Join为什么会导致问题呢,下面是其中一种场景示例: 为什么会出现 NoUniqueKey :…...
TC8:SOMEIP_ETS_027-028
SOMEIP_ETS_027: echoUINT8 目的 检查method方法echoUINT8的参数及其顺序能够被顺利地发送和接收 说白了就是检查UINT8数据类型参数在SOME/IP协议层的序列化与反序列化是否正常。 UINT8相比于测试用例SOMEIP_ETS_021: echoINT8中的SINT8数据类型来说,属于无符号整数,也就是…...
小微企业SaaS ERP管理系统,SpringBoot+Vue+ElementUI+UniAPP
小微企业的SaaS ERP管理系统,ERP系统源码,ERP管理系统源代码 一款适用于小微企业的SaaS ERP管理系统, 采用SpringBootVueElementUIUniAPP技术栈开发,让企业简单上云。 专注于小微企业的应用需求,如企业基本的进销存、询价&#…...
css filter 常用方法函数和应用实例
1. blur() 模糊 filter: blur(半径);参数:模糊半径(像素),值越大越模糊 示例:filter: blur(5px);2. brightness() 亮度 filter: brightness(百分比); 参数:1原始对比度,0全灰,>…...
chrome inspect 调试遇到的问题
1、oppp 手机打开webview 的时候, 报错这个并没有页面 Offline #V8FIG6SGLN75M7FY Pending authentication: please accept debugging session on the device. 解决方法,保持chrome 浏览器在显示的状态 去设置里开启usb 调试再关闭,反复重…...
Kotlin 中 List 和 MutableList 的区别
在 Kotlin 中,List 和 MutableList 是两种不同的集合接口,核心区别在于可变性。 Kotlin 集合框架的重要设计原则:通过接口分离只读(read - only)和可变(mutable)操作,以提高代码的安…...
openssl 生成自签名证书实现接口支持https
1.下载安装openssl Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions 2.配置环境变量 将 openssl 的目录(D:\tools\openssl\bin)添加到 path 中 3.生成自签名证书 找一个存证书的目录打开powershell 3.1 生成私钥 openssl gen…...
React 中集成 Ant Design 组件库:提升开发效率与用户体验
React 中集成 Ant Design 组件库:提升开发效率与用户体验 一、为什么选择 Ant Design 组件库?二、基础引入方式三、按需引入(优化性能)四、Ant Design Charts无缝接入图标前面提到了利用Redux提供全局维护,但如果在开发时再自己手动封装组件,不仅效率不高,可能开发的组件…...
神经网络:节点、隐藏层与非线性学习
神经网络:节点、隐藏层与非线性学习 摘要: 神经网络是机器学习领域中一种强大的工具,能够通过复杂的结构学习数据中的非线性关系。本文从基础的线性模型出发,逐步深入探讨神经网络中节点和隐藏层的作用,以及它们如何…...
vue+tsc+noEmit导致打包报TS类型错误问题及解决方法
项目场景: 提示:这里简述项目相关背景: 当我们新建vue3项目,package.json文件会自动给我添加一些配置选项,这写选项基本没有问题,但是在实际操作过程中,当项目越来越复杂就会出现问题,本文给大家分享vuetscnoEmit导致打包报TS类型错误问题及…...
Ragflow服务器上部署教程
参考官方文档进行整理 克隆相应代码 git clone https://github.com/infiniflow/ragflow.git修改vm.max_map_count sudo sysctl -w vm.max_map_count262144修改 daemon.json文件 {"registry-mirrors": ["https://docker.m.daocloud.io","https://0…...
Ubuntu 系统中解决 Firefox 中文显示乱码的完整指南
Firefox 是一款流行的网络浏览器,但在 Ubuntu 系统中有时会遇到中文显示乱码的问题。本文将为您提供一个全面的解决方案,帮助您轻松解决这个烦人的问题。 问题概述 在 Ubuntu 系统中使用 Firefox 浏览器时,有时会发现中文字符显示为乱码或方块。这通常是由于缺少合适的中文…...
JVM——垃圾回收
垃圾回收 在Java虚拟机(JVM)的自动内存管理中,垃圾回收(Garbage Collection, GC)是其核心组件之一。它负责回收堆内存中不再使用的对象所占用的内存空间,以供新对象的分配使用。下面我们将深入探讨JVM中的…...
【AI News | 20250506】每日AI进展
AI Repos 1、gitsummarize GitSummarize是一个在线工具,用户只需将GitHub URL中的“hub”替换为“summarize”,即可为任何公开或私有代码库生成交互式文档。该工具利用Gemini分析代码结构,自动生成系统级架构概述、目录和文件摘要、自然语言…...
LabVIEW高冲击加速度校准系统
在国防科技领域,高 g 值加速度传感器广泛应用于先进兵器研制,如深侵彻系统、精确打击弹药及钻地弹药等。其性能指标直接影响研究结果的准确性与可靠性,因此对该传感器进行定期校准意义重大。高冲击加速度校准系统具备多方面功能,适…...
优化算法 - intro
优化问题 一般形式 minimize f ( x ) f(\mathbf{x}) f(x) subject to x ∈ C \mathbf{x} \in C x∈C 目标函数 f : R n → R f: \mathbb{R}^n \rightarrow \mathbb{R} f:Rn→R限制集合例子 C { x ∣ h 1 ( x ) 0 , . . . , h m ( x ) 0 , g 1 ( x ) ≤ 0 , . . . , g r …...
从PotPlayer到专业播放器—基于 RTSP|RTMP播放器功能、架构、工程能力的全面对比分析
从PotPlayer到专业播放器SDK:工程项目怎么选择合适的播放方案? ——基于 RTSP、RTMP 播放器功能、架构、工程能力的全面对比分析 在许多音视频项目早期,我们都听过这句话: “本地测试就用 PotPlayer 播吧,能播就行了…...
EasyRTC嵌入式音视频通信SDK技术,助力工业制造多场景实时监控与音视频通信
一、背景 在数字化时代,实时监控广泛应用于安防、工业、交通等领域。但传统监控系统实时性、交互性欠佳,难以满足需求。EasyRTC作为先进实时通信技术,具有低延迟、高可靠、跨平台特性,能有效升级监控系统。融入EasyRTC后…...
MPay码支付系统第四方聚合收款码多款支付插件个人免签支付源码TP8框架全开源
一、源码描述 这是一套码支付源码(MPay),基于TP8框架,前端layui2.9后端PearAdmin,专注于个人免签收款,通过个人的普通收款码,即可实现收款通知自动回调,支持绝大多数商城系统&#…...
wrod生成pdf。[特殊字符]改背景
import subprocess import os,time from rembg import remove, new_session from PIL import Image import io from docxtpl import DocxTemplate, InlineImage from docx.shared import Inches input_folder ‘tupian’ # 输入文件夹 kouchu_folder ‘kouchu’ # 去背景图像…...
动手学深度学习12.1. 编译器和解释器-笔记练习(PyTorch)
以下内容为结合李沐老师的课程和教材补充的学习笔记,以及对课后练习的一些思考,自留回顾,也供同学之人交流参考。 本节课程地址:无 本节教材地址:12.1. 编译器和解释器 — 动手学深度学习 2.0.0 documentation 本节…...
数字文明时代开源技术驱动的商业范式重构:基于开源AI大模型、AI智能名片与S2B2C商城小程序源码的协同创新研究
摘要:数字文明时代,数字技术正以指数级速度重构全球经济与社会结构。本文聚焦开源AI大模型、AI智能名片与S2B2C商城小程序源码的协同创新机制,从技术架构、商业逻辑、实践案例三个维度展开系统研究。基于多行业实证数据,揭示开源技…...
【Bootstrap V4系列】学习入门教程之 组件-轮播(Carousel)
Bootstrap V4系列 学习入门教程之 组件-轮播(Carousel) 轮播(Carousel)一、How it works二、Example2.1 Slides only 仅幻灯片2.2 With controls 带控制装置2.3 With indicators 带指示器2.4 With captions 带字幕 轮播࿰…...
嵌入式openharmony标准鸿蒙系统驱动开发基本原理与流程
第一:鸿蒙概述 OpenHarmony采用多内核(Linux内核或者LiteOS)设计,支持系统在不同资源容量的设备部署。当相同的硬件部署不同内核时,如何能够让设备驱动程序在不同内核间平滑迁移,消除驱动代码移植适配和维护的负担,是OpenHarmony驱动子系统需要解决的重要问题。 …...
Leetcode 刷题记录 08 —— 链表第二弹
本系列为笔者的 Leetcode 刷题记录,顺序为 Hot 100 题官方顺序,根据标签命名,记录笔者总结的做题思路,附部分代码解释和疑问解答,01~07为C语言,08及以后为Java语言。 01 合并两个有序链表 /*** Definition…...
PaddlePaddle 和PyTorch选择与对比互斥
你遇到的错误信息如下: RuntimeError: (PreconditionNotMet) Tensors dimension is out of bound.Tensors dimension must be equal or less than the size of its memory.But received Tensors dimension is 8, memorys size is 0.[Hint: Expected numel() * Size…...
极新月报·2025.4人工智能投融资观察
“ AI投资从‘量’向‘质’过渡 ” 4月重点关注: 1、四月人工智能领域投融资事件105起,披露金额78.63亿人民币。 2、亿级人民币以上金额的投资事件共20起 。 3、四月人工智能领域出现1起IPO事件。 4、在所有融资事件里,除去股权投资&…...
C++ vector 介绍与使用
目录 1.vector是什么? 2.vector的使用 2.1vector的构造函数 2.2vector iterator 的使用 2.3vector 空间增长问题 2.4vector的增删查改 1.vector是什么? 1. vector是表示可变大小数组的序列容器。 2. 就像数组一样,vector也 采用连续的存储…...