从零实现一个高并发内存池 - 2
上一篇https://blog.csdn.net/Small_entreprene/article/details/147904650?fromshare=blogdetail&sharetype=blogdetail&sharerId=147904650&sharerefer=PC&sharesource=Small_entreprene&sharefrom=from_link
高并发内存池 - thread cache
一、基本结构与原理
Thread cache 基于哈希桶结构构建,每个哈希桶均是一个按桶位置映射大小的内存块对象自由链表。系统为每个线程配备独立的 thread cache 对象,使得各线程在该对象上进行内存的获取与释放操作时无需加锁,提升操作效率。
哈希桶是 thread cache 的基础结构,每个哈希桶代表特定大小内存块的集合,每个桶维护一个自由链表,链表中的节点是空闲的内存块对象。图片中的蓝色区域左侧部分展示了不同大小内存块对应的哈希桶,如 8Byte、16Byte、24Byte 等,每个桶通过链表(绿色方块及橙色箭头)连接空闲内存块。
我们实现thread cache主要的流程是:分段对齐---对齐后确定桶的位置,获取桶下的_freeList链表内容---没有就向central cache获取:
以下是细节解析:
对应映射规则
步骤 | 操作 | 说明 |
---|---|---|
分段 | 将内存大小划分为不同的区间段: | |
- 小于等于 128 字节 | ||
- 大于 128 字节且小于等于 1024 字节 | ||
- 大于 1024 字节且小于等于 8 KB(1024 × 8 字节) | ||
- 大于 8 KB 且小于等于 64 KB(1024 × 64 字节) | ||
- 大于 64 KB 且小于等于 256 KB(1024 × 256 字节) | ||
目的:适应不同大小的内存申请需求,提高内存分配的灵活性和效率;减少内碎片,通过为不同大小范围的内存块设置不同的对齐规则,使内存块的大小更加合理,避免因内存块大小不匹配而导致的浪费。 | ||
对齐 | 在每个分段区间内,根据内存块的大小确定对齐方式: | |
- <=128字节:8字节对齐,确保内存块大小是 8 的整数倍。 | ||
- 128<字节<=1024字节:16字节对齐,确保内存块大小是 16 的整数倍。 | ||
- 1024<字节<=8×1024字节(即 8 KB):128字节对齐。 | ||
- 8×1024<字节<=64×1024字节(即 64 KB):1024字节对齐。 | ||
- 64×1024<字节<=256×1024字节(即 256 KB):8×1024字节对齐(8 KB 对齐)。 | ||
目的:内碎片控制,确保每个内存块的大小是其对齐单位的整数倍,减少内碎片,提高内存利用率;硬件兼容性,遵循现代计算机体系结构对内存访问的对齐要求,确保内存块的起始地址满足硬件的对齐规范,提高系统的稳定性和性能。 | ||
桶位置映射 | 通过特定的函数(如 SizeClass::Index)将对齐后的内存块大小映射到哈希桶的位置(即自由链表的索引): | |
1. 确定分段区间:根据申请的内存大小,确定其所属的分段区间。 | ||
2. 计算对齐大小:根据分段区间对应的对齐规则,计算出对齐后的内存块大小。 | ||
3. 映射桶位置:根据对齐后的内存块大小,使用特定的映射规则(如分组数组和位移操作)计算出其对应的哈希桶索引。 | ||
映射的具体实现逻辑: | ||
- <128字节:对齐到 8 字节后,使用 _Index 函数(基于位移和减法操作)计算桶索引。 | ||
- 128到1024字节:对齐到 16 字节后,同样使用 _Index 并结合分组数组偏移计算索引。 | ||
- 1024到8192字节:对齐到 128 字节后,结合前面的分组和偏移确定索引。 | ||
- 8192到65536字节:对齐到 1024 字节后,进行对应的索引计算。 | ||
- 65536到262144字节:对齐到 8192 字节后,确定索引。 | ||
目的:快速定位,通过将内存块大小映射到特定的哈希桶位置,可以快速定位到存储该大小空闲内存块的自由链表,提高内存分配和释放的效率;组织管理,将相同大小(或相近大小)的内存块组织在同一个哈希桶的自由链表中,便于管理和批量操作,减少内存分配和回收时的搜索复杂度。 |
以下是完整流程示例:
步骤 | 内存申请大小 | 分段区间 | 对齐大小 | 对齐规则 | 桶位置索引计算 |
---|---|---|---|---|---|
示例 | 76 字节 | 128 字节 | 80 字节(_RoundUp(76, 16) = 80) | 16 字节对齐 | 使用 _Index 函数结合分组数组偏移计算索引,假设得到索引为 i。 |
这种分段 - 对齐 - 桶位置映射的完整流程,可以实现对内存块的高效管理,优化内存分配和释放性能,同时控制内碎片和提高系统稳定性。
为什么需要分段对齐?
内碎片控制:通过将内存块按照不同的大小范围(如 1 - 128 字节、128 - 1024 字节等)进行分段对齐,可以将内碎片控制在合理的范围内。例如,对于小内存块(<=128 字节),采用 8 字节对齐;对于中等大小内存块(128 - 1024 字节),采用 16 字节对齐。这样可以确保每个内存块的大小都是对齐单位的整数倍,避免内存浪费。如果统一采用较大的对齐单位(如 128 字节),则小内存块的内碎片会显著增加,导致内存利用率下降。
减少外部碎片:分段对齐有助于更好地组织和管理内存块,使得不同大小的内存块能够更高效地被分配和回收,减少外部碎片。例如,当一个线程申请 100 字节的内存时,系统会分配一个 128 字节(经过 8 字节对齐后的大小)的内存块。这种对齐策略使得内存块的大小更加规则,便于后续的内存分配和回收操作。
快速匹配内存块:当线程申请内存时,系统可以根据申请的内存大小快速确定对应的对齐规则和哈希桶位置,从而快速找到合适的空闲内存块。这种分段对齐的方式使得内存分配算法更加高效,减少了在查找合适内存块时的计算复杂度。例如,对于 7 字节的内存申请,系统会将其对齐到 8 字节,并直接定位到对应的哈希桶,快速分配内存。
批量处理内存块:在从 central cache 向 thread cache 批量获取内存块时,分段对齐使得内存块的大小更加统一,便于批量操作和管理。例如,系统可以一次性获取多个 8 字节对齐的内存块,并将它们插入到对应的自由链表中,提高内存分配的效率。
我们内存对齐可以认为是缩小范围,减少空间浪费,没必要对应每一个n字节大小的都对应一个桶,一个_freeList,这样的话128KB就需要2万多个桶,也就是2万多个_freeList,通过分段对齐,我们总共只需要有208个桶位置/_freeList。
二、内存申请流程
-
当申请的内存大小(size)不超过 256KB 时,线程首先获取本地存储的 thread cache 对象,依据特定规则计算出 size 对应的哈希桶自由链表下标 i。
-
若自由链表 _freeLists[i] 中存在可用内存对象,则直接从中弹出(Pop)一个内存对象并将其返回给请求者,完成内存分配。
-
一旦发现 _freeLists[i] 中无可用内存对象,系统将从 central cache 批量获取一定数量的内存对象,将这些对象插入到自由链表后,从中取出一个返回给请求者,以满足内存申请需求。
到了第三步,我们可能会有这样的问题:
我们知道,每一个线程都会有自己独享的thread cache,thread cache里面是一个哈希桶,每个桶里面挂的就是切好的小对象组织起来的自由链表,需要使用对应大小的内存就去找到对应映射位置的桶下的自由链表去取,有的话就直接使用,有的话效率是很高的,没有的话就去下一层去申请,如果是往下申请,那我们当前线程如何获取到的呢?
我们知道在一个进程里面,可能有多个线程,多个线程共享整个进程地址空间,每一个线程有自己独立的栈,寄存器等等,有些东西是共享的,比如说全局数据段,代码段等等,那么每一个线程都要有自己的thread cache,那么这个thread cache又是如何创建的呢?
我们不想要通过锁的方式来解决,那么我们该怎么办呢?其实实际当中真正要通过无锁,我们还需要补充一个知识:tls(不是网络当中的tls)
TLS - thraed local storage(线程本地存储)
TLS(Thread Local Storage,线程本地存储)是一种内存管理机制,用于为每个线程提供独立的变量副本,使得每个线程可以拥有自己的数据,而不会与其他线程的数据发生冲突。与其他线程共享的全局变量或静态变量不同,TLS 变量在每个线程中都是独立的。
linux gcc下 tls
windows下 tls
_declspec(thread)
用于声明线程本地存储变量,每个线程对该变量的访问是独立的,因此不需要锁机制来保护该变量的访问。线程本地存储变量的设计目的就是为了在多线程环境中,为每个线程提供独立的变量副本,从而避免线程之间的竞争和数据不一致问题。
pTLSThreadCache
是一个线程本地存储指针,每个线程都有自己的 pTLSThreadCache
副本,对这个指针的赋值和读取操作是在线程内部进行的,不会与其他线程的操作发生冲突,因此不需要额外的同步措施。
不过,在实际使用中,如果对 pTLSThreadCache
所指向的对象(即 ThreadCache
对象)进行复杂的操作,而这些操作可能涉及多个线程间共享的数据,那么在操作这些共享数据时可能需要使用锁或其他同步机制来保证线程安全。但对于 pTLSThreadCache
指针本身的简单赋值和读取操作,并不需要锁。
我们的详细细节都体现在了实现的代码当中:
Common.h:
#pragma once
#include<iostream>
#include<vector>
#include<ctime>
#include<windows.h>
#include<assert.h>
#include<thread>//方便,不使用using namespace std;是因为防止污染
using std::cout;
using std::endl;static const size_t MAX_BYTES = 256 * 1024;
static const size_t NFREELIST = 208;//总共的桶的数量static void*& NextObj(void* obj)
{return *(void**)obj;
}// 管理切分好的小对象的自由链表
class FreeList
{
public:void Push(void* obj){assert(obj);// 头插//*(void**)obj = _freeList;NextObj(obj) = _freeList;_freeList = obj;}void PushRange(void* start, void* end){NextObj(end) = _freeList;_freeList = start;}void* Pop(){assert(_freeList);// 头删void* obj = _freeList;_freeList = NextObj(obj);return obj;}bool Empty(){return _freeList == nullptr;}private:void* _freeList = nullptr;//这个要写,因为我们没有写构造
};// 计算对象大小的对其映射规则
class SizeClass
{
public:// 提供函数来计算给一个字节数,对应到正确的桶位置;// 规则如下:// 整体控制在最多10%左右的内碎片浪费!!!// ***************************************************************************************************// ***************************************************************************************************// [1,128] 8byte对齐 freelist[0,16) 这个没办法>^<// [128+1,1024] 16byte对齐 freelist[16,72) 15/(129+15)=0.10....// [1024+1,8*1024] 128byte对齐 freelist[72,128) 127/(1025+127)=0.11....// [8*1024+1,64*1024] 1024byte对齐 freelist[128,184) //......// [64*1024+1,256*1024] 8*1024byte对齐 freelist[184,208) //......// ***************************************************************************************************// ***************************************************************************************************//相当于RoundUp的子函数:给定当前size大小和对应规则的对齐数AlignNum,用于处理//static inline size_t _RoundUp(size_t size, size_t alignNum)//{// size_t alignSize;// if (size % alignNum != 0)// {// alignSize = (size / alignNum + 1) * alignNum;// }// else//等于0就不需要处理了,已经对齐了// {// alignSize = alignNum;// }//}//上面的是我们普通人玩出来的,下面我们来看看高手是怎么玩的!static inline size_t _RoundUp(size_t bytes, size_t alignNum){return ((bytes + alignNum - 1) & ~(alignNum - 1));}//你给我一个size,就需要算出对其以后是多少 --- 比如说:8->8 7->8static inline size_t RoundUp(size_t size){if (size <= 128){return _RoundUp(size, 8);}else if (size <= 1024){return _RoundUp(size, 16);}else if (size <= 8 * 1024){return _RoundUp(size, 128);}else if (size <= 64 * 1024){return _RoundUp(size, 1024);}else if (size <= 256 * 1024){return _RoundUp(size, 8 * 1024);}else{//说明大于256KB了,那么就有点问题了assert(false);return -1;}}//一样的,我们来自己写写看,待会换成别人的刚好的方式//size_t _Index(size_t bytes, size_t alignNum)//{// if (bytes % alignNum == 0)// {// return bytes / alignNum - 1;// }// else// {// return bytes / alignNum;// }//}//高手的想法:static inline size_t _Index(size_t bytes, size_t align_shift){return ((bytes + (1 << align_shift) - 1) >> align_shift) - 1;}// 计算映射的哪一个自由链表桶static inline size_t Index(size_t bytes){assert(bytes <= MAX_BYTES);// 每个区间有多少个链static int group_array[4] = { 16, 56, 56, 56 };if (bytes <= 128) {return _Index(bytes, 3);}else if (bytes <= 1024) {return _Index(bytes - 128, 4) + group_array[0];}else if (bytes <= 8 * 1024) {return _Index(bytes - 1024, 7) + group_array[1] + group_array[0];}else if (bytes <= 64 * 1024) {return _Index(bytes - 8 * 1024, 10) + group_array[2] + group_array[1] + group_array[0];}else if (bytes <= 256 * 1024) {return _Index(bytes - 64 * 1024, 13) + group_array[3] + group_array[2] + group_array[1] + group_array[0];}else {assert(false);}return -1;}
private:};
ThreadCache.h:
#pragma once
#include"Common.h"class ThreadCache
{
public:// 申请和释放内存对象void* Allocate(size_t size);void Deallocate(void* ptr, size_t size);// 从中心缓存获取对象void* FetchFromCentralCache(size_t index, size_t size);
private:FreeList _freeLists[NFREELIST];//_freeLists 是一个数组,用于存储多个 FreeList 类型的对象。
};
//线程隔离 :每个线程对 pTLSThreadCache 的访问都是独立的,一个线程对其的修改不会影响其他线程中的该变量值。
//避免锁竞争 :由于每个线程都有自己的变量副本,避免了多线程环境下因共享变量而导致的锁竞争问题,提高了程序的并发性能。
//方便线程专用数据管理 :适合存储线程专用的资源或状态信息,例如每个线程的缓存、配置等。
static _declspec(thread) ThreadCache* pTLSThreadCache = nullptr;
//实际中,不可能让每一个线程自己来获取ThreadCache*这个对象,我们还需要再封装一层——————》ConcurrentAlloc.h
//也就是说:一个线程起来了,并不是马上就有pTLSThreadCache了,而是需要去调用相关的函数
ThreadCache.cpp:
#include"ThreadCache.h"//声明与实现分离void* ThreadCache::FetchFromCentralCache(size_t index, size_t size)
{//...return nullptr;
}void* ThreadCache::Allocate(size_t size)
{assert(size <= MAX_BYTES);//并不是所有要求申请的字节数都有完全对应的一个桶位,是1字节,7字节都是放在对应的8字节桶中,那么我们就需要实现一个对应的对其映射的规则:我们在Common.h中实现size_t alignSize = SizeClass::RoundUp(size);//对齐数size_t index = SizeClass::Index(alignSize);//计算出了对齐数,那么又是对应的哪一个桶呢?//通过TLS,每个线程无锁的获取自己专属的ThreadCache对象if (!_freeLists[index].Empty()){return _freeLists->Pop();}else//这个桶下面的自由链表为空,为空只能向下一层去要了{return FetchFromCentralCache(index, alignSize);}
}void ThreadCache::Deallocate(void* ptr, size_t size)//free只需要传入对应要free空间的指针就可以了,但是为了知道放入到对应的正确的桶位置,需要size参数来定位桶的位置
{assert(ptr);assert(size <= MAX_BYTES);//找出对应的自由链表的桶,插入进去size_t index = SizeClass::Index(size);_freeLists[index].Push(ptr);
}
ConcurrentAlloc.h:
#pragma once
#include"Common.h"
#include"ThreadCache.h"//搞成全局静态,不然包在多个.cpp当中的话,静态的保持在当前文件可见,否则全局的不加上静态,其链接属性就会冲突(因为一个.h会形成一个obj,所包含的可能最终会导致冲突!)
static void* ConcurrentAlloc(size_t size)
{if (pTLSThreadCache == nullptr){pTLSThreadCache = new ThreadCache;}cout << std::this_thread::get_id() << ":" << pTLSThreadCache << endl;return pTLSThreadCache->Allocate(size);
}static void ConcurrentFree(void* ptr, size_t size)//我们传入size参数的问题更后面整体联系起来了再解决!!!
{assert(pTLSThreadCache);pTLSThreadCache->Deallocate(ptr, size);
}
注意:在创建线程时,线程缓存的初始化也是从 central cache 获取内存的,这是因为线程缓存本身不存储内存块,它需要从 central cache 获取内存来初始化自己的自由链表。当线程申请内存时,如果线程缓存的自由链表为空,则也会从 central cache 获取内存。这种机制确保了线程缓存中有足够的内存块来满足线程的内存申请需求,同时减少了从 central cache 获取内存的频率,提高了性能。(一点要理清楚哦)
三、内存释放流程
-
对于释放的内存大小小于 256k 的情况,会将内存释放回 thread cache。此时,同样根据内存块的大小计算出对应的自由链表桶位置 i,将待释放的内存对象推入(Push)到 _freeLists[i] 中,完成内存释放。
-
在内存释放过程中,若发现自由链表的长度超出设定阈值,即链表过长时,会触发内存回收机制,将部分内存对象从 thread cache 回收至 central cache,以优化内存的组织与管理,避免 thread cache 内内存对象的过度堆积。
都实现在了上面的代码当中!!!
接下来,我们就需要往下层挖掘,下一篇,我们精彩继续:central cache!
相关文章:
从零实现一个高并发内存池 - 2
上一篇https://blog.csdn.net/Small_entreprene/article/details/147904650?fromshareblogdetail&sharetypeblogdetail&sharerId147904650&sharereferPC&sharesourceSmall_entreprene&sharefromfrom_link 高并发内存池 - thread cache 一、基本结构与原…...
promise
handleFileChange(event) {var that this;// 处理文件上传并传递回调函数this.$commonJS.handleFileUpload(event, function (tables) {console.log(tables at line 786:, tables);// 使用 Promise.all 等待所有表格解析完成Promise.all(tables.map((table) > {return new …...
Django + Celery 打造企业级大模型异步任务管理平台 —— 从需求到完整实践(含全模板源码)
如需完整工程文件(含所有模板),可回复获取详细模板代码。 面向人群:自动化测试工程师、企业中后台开发人员、希望提升效率的 AI 业务从业者 核心收获:掌握 Django 三表关系设计、Celery 异步任务实践、基础 Web 交互与前后端分离思路,源码可直接落地,方便二次扩展 一、系…...
从零开始完成“大模型在牙科诊所青少年拉新系统中RAG与ReACT功能实现”的路线图
项目核心目标: 构建一个智能系统,利用大型语言模型(LLM)、检索增强生成(RAG)和推理与行动(ReACT)技术,通过七个专门的知识向量库,为牙科诊所精准吸引青少年客…...
c# 倒序方法
在C#中,有几种方法可以对List进行倒序排列: 1. 使用List的Reverse()方法(原地反转) List<int> numbers new List<int> { 1, 2, 3, 4, 5 };numbers.Reverse(); // 直接修改原列表// 结果:5, 4, 3, 2, 1 …...
【!!!!终极 Java 中间件实战课:从 0 到 1 构建亿级流量电商系统全链路解决方案!!!!保姆级教程---超细】
终极 Java 中间件实战课:电商系统架构实战教程 电商系统架构实战教程1. 系统架构设计1.1 系统模块划分1.2 技术选型 2. 环境搭建2.1 开发环境准备2.2 基础设施部署 3. 用户服务开发3.1 创建Maven项目3.2 创建用户服务模块3.3 配置文件3.4 实体类与数据库设计3.5 DAO…...
HarmonyOs开发之———使用HTTP访问网络资源
谢谢关注!! 前言:上一篇文章主要介绍HarmonyOs开发之———Video组件的使用:HarmonyOs开发之———Video组件的使用_华为 video标签查看-CSDN博客 HarmonyOS 网络开发入门:使用 HTTP 访问网络资源 HarmonyOS 作为新一代智能终端…...
Java Queue 接口实现
Date: 2025.05.14 20:46:38 author: lijianzhan Java中的Queue接口是位于java.util包中,它是一个用于表示队列的接口。队列是一种先进先出(First-In-First-Out, 简称为FIFO)的数据结构,其中元素被添加到队列的尾部,并从…...
【IDEA】注释配置
1. IDEA注释调整,去掉默认在第一列显示 修改为如下: 2. IDEA中修改代码中的注释颜色...
day25 python异常处理
目录 Python 的异常处理机制 核心概念 常见的异常处理结构 try-except try-except-else 常见异常类型 SyntaxError(语法错误) NameError(名称错误) TypeError(类型错误) ValueError(值…...
【测试】BUG
目录 1、描述BUG的要素: 2、BUG的级别 3、BUG的状态的流转 4、与开发产⽣争执怎么办(⾼频考题) 什么是BUG??? 程序与规格说明之间的不匹配才是错误 1、描述BUG的要素: 问题出现的版本、问…...
Java面向对象三大特性深度解析
Java面向对象三大特性封装继承多态深度解析 前言一、封装:数据隐藏与访问控制的艺术1.1 封装的本质与作用1.2 封装的实现方式1.2.1 属性私有化与方法公开化1.2.2 封装的访问修饰符 二、继承:代码复用与类型扩展的核心机制2.1 继承的定义与语法2.2 继承的…...
C 语言学习笔记(8)
内容提要 数组 数组的概念一维数组 数组 数组的概念 什么是数组 数组是相同类型,有序数据的集和 数组的特征 数组中的数据被称之为数组的元素(所谓的元素,其实就是数组的每一个匿名的变量空间),是同构。数组中的…...
Screen Mirroring App:轻松实现手机与电视的无缝投屏
Screen Mirroring App 是一款由2kit consulting发行的电视投屏软件,专为用户提供便捷的投屏解决方案。它支持将手机和平板屏幕上的内容实时投射到大电视上,无论是观看影视作品、玩游戏、浏览照片还是阅读电子书,都能提供清晰、稳定的画质和流…...
html js 原生实现web组件、web公共组件、template模版插槽
在现代浏览器中,通过 class 继承 HTMLElement 可以轻松创建原生 Web Components(自定义元素),并能享受与普通 HTML 元素同等的语义和性能优势。下面将从核心概念、生命周期方法、Shadow DOM、表单关联、自定义属性、以及与 Vue 3 …...
【爬虫】DrissionPage-2
之前的三个对象是4.0版本,看到的是网上大佬们网上的文章,因为看到官网更新了4.1,我觉得有必要了解一下:文档地址:💥 4.1 功能介绍 | DrissionPage官网 点击链接看官网就行,下面一样的。 4.1 的…...
鸿蒙OSUniApp 制作个人信息编辑界面与头像上传功能#三方框架 #Uniapp
UniApp 制作个人信息编辑界面与头像上传功能 前言 最近在做一个社交类小程序时,遇到了需要实现用户资料编辑和头像上传的需求。这个功能看似简单,但要做好用户体验和兼容多端,还是有不少细节需要处理。经过一番摸索,总结出了一套…...
系统漏洞扫描服务:维护网络安全的关键与服务原理?
系统漏洞扫描服务是维护网络安全的关键措施,能够迅速发现系统中的潜在风险,有效预防可能的风险和损失。面对网络攻击手段的日益复杂化,这一服务的重要性日益显著。 服务原理 系统漏洞扫描服务犹如一名恪尽职守的安全守护者。它运用各类扫描…...
[原创](现代C++ Builder 12指南): 在界面开发中, 如何利用C++高级特性“折叠表达式“?.
[序言] 在现代C++编程中, 现代C++引入的折叠表达式(Fold Expressions)是一项极具价值的特性, 它为模板编程带来了更高的灵活性和简洁性. 折叠表达式允许在参数包上执行简洁的折叠操作, 从而减少冗余代码, 提升代码的可读性与维护性. 在界面开发领域, 特别是使用C++ Builder 12进…...
KUKA机器人中断编程3—暂停功能的编程
在KUKA机器人的使用过程中,对于调试一个项目,当遇到特殊情况时需要暂停机器人,等异常情况处理完成后再继续机器人的程序运行。wait for指令是等待一个输入信号指令,没有输入信号,机器人一直等待。在一定程度上程序也不…...
【LeetCode 热题 100】反转链表 / 回文链表 / 有序链表转换二叉搜索树 / LRU 缓存
⭐️个人主页:小羊 ⭐️所属专栏:LeetCode 热题 100 很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~ 目录 相交链表反转链表回文链表环形链表环形链表 II合并两个有序链表两数相加删除链表的倒数第 N 个结点两两交换链表中的…...
Seata源码—1.Seata分布式事务的模式简介
大纲 1.Seata分布式事务框架简介 2.Seata AT模式实现分布式事务的机制 3.Seata AT模式下的写隔离机制 4.Seata AT模式下的读隔离机制 5.官网示例说明Seata AT模式的工作机制 6.Seata TCC模式的介绍以及与AT模式区别 7.Seata Saga模式的介绍 8.单服务多个库的分布式事务…...
牛客——签到题
分析 我拿到题就去看了示例,可以发现,并非是让难度最小,或者难度系数出现次数最多的成为签到题的难度。那我就有点懵了。。。。。。 但仔细观察题目本身的特定条件和目标,即在满足选择 m 道题的前提下,尽可能多地选择…...
【idea】调试篇 idea调试技巧合集
前言:之前博主写过一篇idea技巧合集的文章,由于技巧过于多了,文章很庞大,所以特地将调试相关的技巧单独成章, 调试和我们日常开发是息息相关的,用好调试可以事半功倍 文章目录 1. idea调试异步线程2. idea调试stream流…...
k8s监控方案实践补充(一):部署Metrics Server实现kubectl top和HPA支持
k8s监控方案实践补充(一):部署Metrics Server实现kubectl top和HPA支持 文章目录 k8s监控方案实践补充(一):部署Metrics Server实现kubectl top和HPA支持一、Metrics Server简介二、Metrics Server实战部署…...
直流电机风速仪
在处理直流电机风速仪的 ADC 读取问题时,下面为你详细介绍实现方法。 硬件连接 风速仪的输出通常是模拟信号,所以需要把它连接到微控制器的 ADC 输入引脚。比如,在 Arduino 上可以连接到 A0 - A5 这类模拟输入引脚。 ADC 读取原理 风速仪…...
dify 连接不上ollama An error occurred during credentials validation:
三大报错 An error occurred during credentials validation: HTTPConnectionPool(hosthost.docker.internal, port11434): Max retries exceeded with url: /api/chat (Caused by NameResolutionError("<urllib3.connection.HTTPConnection object at 0x7f26fc3c00b0&…...
19、云端工业物联网生态组件 - 工厂能效与预测维护 - /数据与物联网组件/cloud-iiot-factory-analysis
76个工业组件库示例汇总 云端工业物联网生态组件 - 工厂能效与预测维护 (模拟) 概述 这是一个交互式的 Web 组件,旨在模拟一个云端工业物联网 (IIoT) 平台的核心界面,专注于工厂层面的能效分析和基于传感器数据的预测性维护概念。用户可以监控模拟的设…...
python打卡day25
python的异常处理机制 知识点回顾: 异常处理机制debug过程中的各类报错try-except机制try-except-else-finally机制 在即将进入深度学习专题学习前,我们最后差缺补漏,把一些常见且重要的知识点给他们补上,加深对代码和流程的理解。…...
Jmeter变量传递介绍
文章目录 一、Jmeter变量类型及作用域二、变量传递方式1. 用户定义变量(User Defined Variables)2. CSV 数据文件(CSV Data Set Config)3.正则表达式提取器4.后置处理器(Post Processor)4.1BeanShell/JSR223 后置处理器…...
机器学习 Day16 聚类算法 ,数据降维
聚类算法 1.简介 1.1 聚类概念 无监督学习:聚类是一种无监督学习算法,不需要预先标记的训练数据 相似性分组:根据样本之间的相似性自动将样本归到不同类别 相似度度量:常用欧式距离作为相似度计算方法 1.2 聚类vs分类 聚类&…...
白日梦:一个方便快捷的将故事制作成视频的工具
我有故事,但我想把它制作成视频,有没有什么好用的工具可以使用呢?如果你也被类似的问题困扰,那么今天分享的这个工具将会解决这个问题。从需求来看,我们希望的是纯文本的故事输入,完整的故事视频输出&#…...
ultralytics中tasks.py---parse_model函数解析
一、根据scale获取对应的深度、宽度和最大通道数 具体例如yaml文件内容如下: depth=0.33,那么重复的模块例如C2f原本重复次数是3,6,6,3,那么T对应的模型重复次数就是三分之一即1,1,2,1次。这个在后面定义的: width=0.25,max_channels=1024 原本c2=64,但经过make_div…...
Codeforces Round 1003 (Div. 4)
A. Skibidus and Amog’u 题目大意 给你一个字符串,把末尾的us换成i 解题思路 删掉最后两个加上“i”即可 代码实现 #include <bits/stdc.h>using i64 long long;int main() {std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);int …...
基于RFSOC ZU28DR+DSP 6U VPX处理板
板卡概述 基于RFSOC ZU28DRDSP 6U VPX处理板,是一款基于6U VPX总线架构的高速信号处理平台,数模混合信号处理卡,采用 Xilinx ZYNQ UltraScale RFSoC ZU28DR和TI DSP TMS320C6678组合设计,两者之间通过4x 5G SRIO互联。本板卡可实…...
C# 通过脚本实现接口
以前C#脚本用的委托注入模式,今天在AI提示下,尝试用脚本直接实现接口,然后C#可以动态或指定新类型创建接口实现对象。从代码角度看,稍显复杂,但脚本方面显得更简洁和有条理。 引用包需要Microsoft.CodeAnalysis、Micro…...
代码随想录算法训练营Day58
力扣695.岛屿的最大面积【medium】 力扣827.最大人工岛【hard】 一、力扣695.岛屿的最大面积【medium】 题目链接:力扣695.岛屿的最大面积 视频链接:代码随想录 1、思路 和岛屿数量那道题很像,只是递归这边要多一个怎么计算面积,…...
若依框架页面
1.页面地址 若依管理系统 2.账号和密码 管理员 账号admin 密码admin123 运维 账号yuwei 密码123456 自己搭建的地址方便大家学习,不要攻击哦,谢谢啊...
redis 缓存穿透,缓存击穿,缓存雪崩
一:什么是缓存 (1)计算机:cpu、内存、磁盘,cpu任何需要的数据都要从内容中读入数据放入cpu,从cup内部添加一个缓存 (2)web开发的每个阶段都可以添加缓存 (3)缓存优缺点&a…...
ORACLE查看归档是否打开
一、使用V$DATABASE视图 SELECT log_mode FROM v$database; 结果说明: ARCHIVELOG - 数据库处于归档模式 NOARCHIVELOG - 数据库处于非归档模式 二、 使用v$instance视图 SELECT archiver FROM v$instance; 结果说明: STARTED - 归档进程已启动(归档模…...
Python环境管理工具深度指南:pip、Poetry、uv、Conda
Python环境管理工具深度指南:pip、Poetry、uv、Conda Python开发中,环境管理和依赖管理是不可避开的重要话题。合理地管理项目的Python环境(尤其是虚拟环境)有助于隔离不同项目的依赖,避免版本冲突,并确保…...
高等数学第七章---微分方程(§7.4-§7.5可降阶的高阶微分方程、二阶线性微分方程)
7.4 可降阶的高阶微分方程 某些类型的高阶微分方程可以通过适当的变量代换,将其阶数降低,从而化为阶数较低的方程进行求解。 一、 y ( n ) f ( x ) y^{(n)}f(x) y(n)f(x) 型方程 特征:方程的左端是 y y y 的 n n n 阶导数,右…...
Jmeter对服务端进行压测快速上手
安装 下载 安装jmeter的之前必须先装有JDK 官网下载地址:https://archive.apache.org/dist/jmeter/binaries/ jmeter3.0的对应jdk1.7,jmeter4.0对应jdk1.8以上,否者启用jmeter也会报错 配置 配置环境变量 在系统变量PATH上加上: %JMET…...
【嵌入模型与向量数据库】
目录 一、什么是向量? 二、为什么需要向量数据库? 三、向量数据库的特点 四、常见的向量数据库产品 FAISS 支持的索引类型 vs 相似度 五、常见向量相似度方法对比 六、应该用哪种 七、向量数据库的核心逻辑 🔍 示例任务:…...
鸿蒙OSUniApp 开发实时聊天页面的最佳实践与实现#三方框架 #Uniapp
使用 UniApp 开发实时聊天页面的最佳实践与实现 在移动应用开发领域,实时聊天功能已经成为许多应用不可或缺的组成部分。本文将深入探讨如何使用 UniApp 框架开发一个功能完善的实时聊天页面,从布局设计到核心逻辑实现,带领大家一步步打造专…...
React构建组件
React构建组件 React 组件构建方式详解 React 组件的构建方式随着版本迭代不断演进,目前主要有 函数组件 和 类组件 两种核心模式,并衍生出多种高级组件设计模式。以下是完整的构建方式指南: 文章目录 React构建组件React 组件构建方式详解…...
auto.js面试题及答案
以下是常见的 Auto.js 面试题及参考答案,涵盖基础知识、脚本编写、运行机制、权限、安全等方面,适合开发岗位的技术面试准备: 一、基础类问题 什么是 Auto.js?它的主要用途是什么? 答案: Auto.js 是一个…...
OPC UA + ABP vNext 企业级实战:高可用数据采集框架指南
🚀📊 OPC UA ABP vNext 企业级实战:高可用数据采集框架指南 🚀 📑 目录 🚀📊 OPC UA ABP vNext 企业级实战:高可用数据采集框架指南 🚀一、前言 🎯二、系统…...
【springcloud学习(dalston.sr1)】Ribbon负载均衡(七)
该系列项目整体介绍及源代码请参照前面写的一篇文章【springcloud学习(dalston.sr1)】项目整体介绍(含源代码)(一) (一)Ribbon 负载均衡的理解 ribbon是一种客户端的负载均衡。类似于比如我们在火车站窗口…...
编程题 03-树1 树的同构【PAT】
文章目录 题目输入格式输出格式输入样例1(对应图一)输出样例1输入样例2(对应图二)输出样例2 题解解题思路完整代码 编程练习题目集目录 题目 给定两棵树 T 1 T_1 T1 和 T 2 T_2 T2 。如果 T 1 T_1 T1 可以通过若干次左右…...