C++高性能内存池实现:多线程无锁分配与性能优化实战
1. 为什么我要自己造一个内存池第一次被内存分配拖垮性能是在一个日志采集模块上。那个模块逻辑不复杂就是不断接收上游推过来的数据包解析、拼装、落盘。单线程跑的时候吞吐量还行一上多线程压测CPU 占用直接飙到百分之百但实际有效计算时间不到三成。用性能分析工具一挂发现大量时间耗在malloc和free上锁竞争激烈得离谱。这件事让我意识到默认的通用内存分配器在高频、小对象、多线程场景下往往就是性能瓶颈本身。系统自带的分配器要兼顾各种尺寸、各种生命周期、各种线程模型通用性拉满的代价就是单场景效率打折。于是我开始琢磨自己写一个内存池专门服务那些“小块、频繁申请释放、生命周期短”的对象。这篇文章就把我这套 C 高性能内存池的完整实现思路摊开讲。从整体架构怎么定、核心数据结构怎么选、多线程怎么减锁到实际写代码时的参数计算、踩过的坑、排查问题的套路我都会一条条说清楚。适合有一定 C 基础、写过高并发服务、被内存分配性能问题折磨过的同学。哪怕你只是想搞明白内存池到底在解决什么问题看完也能有个清晰的认知。先说清楚这个内存池能干什么它把内存按固定大小切块管理申请释放走无锁或低锁路径单次分配耗时从通用分配器的几十上百纳秒压到个位数纳秒级别多线程下几乎不随线程数增加而劣化。下面进入正题。2. 整体架构设计与方案选型2.1 内存池到底在解决什么问题要理解内存池的价值得先明白通用分配器慢在哪。malloc这类分配器内部维护着复杂的空闲链表、尺寸分类、合并策略还要处理碎片整理。每次分配都要走一遍查找合适块的流程释放时还要判断能不能和相邻块合并。单线程下这些开销还能接受一旦多线程同时申请分配器内部的全局锁就成了兵家必争之地。内存池的思路很直接既然我知道自己要分配的对象大小固定、数量可控那就提前把内存切好申请时直接从空闲链表摘一个释放时挂回去。没有查找、没有合并、没有复杂的尺寸计算就是指针操作。这就是它快的根本原因。我选的是定长块 分级管理的方案。所谓定长块就是每个内存池只负责一种固定大小的内存块比如 64 字节、128 字节、256 字节各一个池子。分级管理则是把不同大小的池子组织起来申请时根据请求大小路由到对应的池子。这样做的好处是每个池子内部逻辑极简坏处是如果对象大小种类太多池子数量会膨胀。实际工程里对象大小通常集中在几个档位所以这个代价可以接受。2.2 为什么不用现成的方案有人会问现成的内存池库不少为什么还要自己写。我的考虑有三点。第一可控性。第三方库的黑盒行为在出问题时很难排查尤其是内存相关的 bug一旦涉及库内部逻辑调试成本极高。自己写的代码每一行都清楚出问题能快速定位。第二针对性优化。通用库要兼顾的场景太多而我的业务场景很明确小对象、高频、多线程、生命周期短。针对这个场景可以做很多通用库不会做的取舍比如放弃大对象支持、放弃跨线程释放的复杂处理。第三学习价值。内存管理是 C 里最能体现功力的领域之一自己实现一遍对指针、对齐、原子操作、缓存友好性的理解会上一个台阶。当然如果你的场景是对象大小变化极大、生命周期长短不一那自己写内存池可能得不偿失用通用分配器或者成熟的内存管理方案更稳妥。方案选型永远要看场景没有银弹。2.3 核心架构分层我的内存池分三层。最底层是内存块存储层负责向系统申请大块内存然后按固定大小切分。这一层只在池子初始化或者扩容时工作运行期几乎不参与。中间层是空闲链表层维护所有未被使用的内存块。申请时从链表头摘一个释放时挂回链表头。这一层是性能关键路径所有优化都围绕它展开。最上层是池管理器层负责根据请求大小路由到对应的池子管理多个不同尺寸的池子处理池子的创建和销毁。这三层职责清晰每层只做一件事。底层不关心谁在用内存中间层不关心内存从哪来上层不关心块怎么管理。这种分层让代码好维护也让每层可以独立优化。3. 核心数据结构与关键实现细节3.1 空闲链表的设计与指针复用技巧空闲链表是内存池的心脏。最朴素的做法是维护一个数组或者链表每个节点存一个指向空闲块的指针。但这样有个问题每个空闲块本身需要额外的存储空间来存“下一个空闲块在哪”这个信息。我的做法是指针复用空闲块本身的内存就用来存下一个空闲块的地址。因为块空闲的时候它的内存内容本来就没用拿来存指针正好。这样每个块不需要额外的管理开销内存利用率拉满。具体实现上空闲块的前 8 个字节64 位系统下指针大小存下一个空闲块的地址。申请时取出头节点把头节点更新为它的 next释放时把当前头节点地址写进待释放块的前 8 字节然后把这个块设为新的头节点。struct FreeNode { FreeNode* next; }; class FreeList { FreeNode* head_ nullptr; public: void push(void* block) { auto* node static_castFreeNode*(block); node-next head_; head_ node; } void* pop() { if (!head_) return nullptr; FreeNode* node head_; head_ head_-next; return node; } };这段代码简单到不能再简单但它是整个内存池性能的基石。没有分支预测失败、没有复杂计算就是两次指针赋值。注意指针复用要求块大小至少能放下一个指针。在 64 位系统上块大小不能小于 8 字节。实际使用中块大小通常远大于这个值所以不是问题。但如果你的场景真有极小对象需要单独处理。3.2 内存对齐的处理内存对齐是个容易被忽视但很关键的点。CPU 访问未对齐的内存时可能需要多次读取再拼接性能下降明显某些架构上甚至直接崩溃。所以内存池返回的块地址必须满足对齐要求。我的做法是块大小向上取整到对齐边界的整数倍。比如对齐要求是 8 字节请求块大小是 50 字节那实际块大小取 56 字节。这样每个块的起始地址天然对齐。constexpr size_t ALIGN 8; size_t alignUp(size_t size) { return (size ALIGN - 1) ~(ALIGN - 1); }这里用位运算代替取模是因为位运算更快。~(ALIGN - 1)在 ALIGN 是 2 的幂时正好是低位清零的掩码。这个技巧在处理对齐、分页、哈希取模时都常用值得记住。对齐边界选多少合适8 字节是 64 位系统的基本对齐要求能放下所有基本类型和指针。如果对象里有double或者 SIMD 类型可能需要 16 字节甚至 32 字节对齐。对齐边界越大内存浪费越多但访问效率越高。我一般默认 8 字节特殊场景再调。3.3 多线程下的锁策略选择多线程是内存池最棘手的地方。最简单的做法是加一把大锁所有申请释放都串行化。但这样多线程性能直接退化成单线程内存池的意义就没了。我试过几种方案最后选的是线程本地缓存 全局池的混合策略。每个线程维护一个自己的空闲链表申请时优先从本地链表拿。本地链表空了才去全局池批量拿一批块回来。释放时也是先还到本地链表本地链表积累到一定数量再批量还回全局池。这样大部分操作都在线程本地完成完全无锁只有批量交换时才需要加锁锁竞争频率大幅降低。thread_local FreeList localList; constexpr size_t BATCH_SIZE 64; void* allocate() { void* block localList.pop(); if (!block) { // 本地空了从全局批量拿 globalPool.refill(localList, BATCH_SIZE); block localList.pop(); } return block; }批量大小怎么定太小了频繁访问全局池锁竞争还是多太大了本地缓存占用内存多而且线程退出时归还的块也多。我实测下来 64 是个不错的平衡点既能把锁竞争降到很低内存占用也可控。当然这个值要根据实际场景调对象越大、线程越多批量值可以适当调小。提示thread_local变量在线程退出时会自动析构但析构顺序和时机有讲究。如果内存池本身在线程退出前就被销毁了线程本地缓存归还时就会访问已释放的内存。这个问题后面排查章节会详细说。4. 完整实操流程与核心代码实现4.1 池子的初始化与扩容池子初始化时先向系统申请一大块内存然后按块大小切分把所有块串成空闲链表。这里有个参数要算一次申请多大内存。申请太小块用完了要频繁扩容每次扩容都要向系统要内存开销大。申请太大内存占用高而且如果实际用不到那么多就浪费了。我的经验值是一次申请能容纳 1024 个块的内存。这个数量下扩容频率很低内存占用也可接受。class MemoryPool { size_t blockSize_; char* memoryStart_; size_t totalBlocks_; FreeList freeList_; public: MemoryPool(size_t blockSize, size_t blockCount 1024) : blockSize_(alignUp(blockSize)), totalBlocks_(blockCount) { size_t totalSize blockSize_ * totalBlocks_; memoryStart_ static_castchar*(::operator new(totalSize)); // 切分并串成链表 for (size_t i 0; i totalBlocks_; i) { void* block memoryStart_ i * blockSize_; freeList_.push(block); } } };注意这里用的是::operator new而不是malloc。区别在于operator new在分配失败时会抛异常而malloc返回空指针。内存池这种底层设施我倾向于用异常因为分配失败通常意味着系统资源耗尽继续跑下去也没意义。扩容逻辑就是再申请一块内存切分后把新块挂到空闲链表上。这里要注意扩容后的内存块和原来的内存块不连续但这不影响使用因为空闲链表本来就是靠指针串联的不依赖物理连续性。4.2 分配与释放的完整路径分配路径分三步先算对齐后的大小路由到对应池子从池子拿块。void* allocate(size_t size) { size_t alignedSize alignUp(size); MemoryPool* pool getPool(alignedSize); return pool-allocate(); }getPool负责根据大小找到对应池子。我用的是向上取整到最近的尺寸档位。比如档位是 16、32、64、128、256请求 50 字节就路由到 64 字节的池子。档位设计有讲究太密了池子多管理复杂太疏了内存浪费大。我一般用 2 的幂次作为档位这样路由可以用位运算快速算出来。MemoryPool* getPool(size_t size) { // 找到不小于 size 的最小 2 的幂 size_t index 0; size_t s size - 1; while (s 0) { s 1; index; } return pools_[index]; }释放路径更简单根据块地址找到它属于哪个池子然后还回去。但这里有个问题怎么从块地址反推池子如果每个池子管理的内存是连续的一大块那可以用地址范围判断。但扩容后内存不连续这个方法就失效了。我的解法是在每个块前面加一个头部记录这个块属于哪个池子。头部占 8 字节存池子指针。释放时读头部就知道该还给谁。代价是每个块多 8 字节开销但换来释放路径的简洁和正确。struct BlockHeader { MemoryPool* pool; }; void* allocate() { void* block freeList_.pop(); if (!block) { refill(); block freeList_.pop(); } auto* header static_castBlockHeader*(block); header-pool this; return static_castchar*(block) sizeof(BlockHeader); } void deallocate(void* ptr) { auto* header reinterpret_castBlockHeader*( static_castchar*(ptr) - sizeof(BlockHeader)); header-pool-freeList_.push(header); }这里返回给用户的地址是跳过头部之后的地址用户拿到的是干净的内存。释放时用户传回来的也是这个地址我们减掉头部大小就找回了原始块。注意头部方案会让实际块大小增加 8 字节计算池子容量时要把这个算进去。另外头部里的池子指针在池子销毁后就成了野指针所以池子销毁前必须确保所有块都已归还或者干脆不销毁池子进程退出时统一回收。4.3 线程本地缓存的批量交换实现线程本地缓存和全局池的批量交换是性能关键。我实现了一个refill方法从全局池一次性拿一批块到本地。void refill(FreeList local, size_t batchSize) { std::lock_guardstd::mutex lock(mutex_); for (size_t i 0; i batchSize; i) { void* block freeList_.pop(); if (!block) break; local.push(block); } }归还时类似本地链表积累到阈值就批量还回全局池。void flush(FreeList local, size_t threshold) { size_t count 0; FreeList temp; while (void* block local.pop()) { temp.push(block); if (count threshold) break; } std::lock_guardstd::mutex lock(mutex_); while (void* block temp.pop()) { freeList_.push(block); } }这里有个细节批量归还时我先把本地链表的一部分摘下来放到临时链表再一次性加锁还回去。这样锁的持有时间最短只包含链表拼接操作不包含遍历。如果直接在锁内遍历本地链表锁持有时间会随批量大小线性增长多线程下还是会有竞争。批量阈值设多少我用的和 refill 一样的 64。本地链表超过 64 个块就触发归还。这样本地缓存最多占 64 个块的内存不会无限膨胀。5. 性能实测与调优经验5.1 测试环境与对比方法我搭了个简单的测试单线程和多线程下分别用系统分配器和我的内存池做一百万次分配释放记录总耗时。对象大小选 64 字节这是日志、网络包这类场景的典型大小。测试代码逻辑就是循环里allocate然后deallocate中间不做其他事纯粹测分配释放开销。多线程版本开 4 个线程每个线程做同样次数的操作。5.2 实测数据与结果分析单线程下系统分配器一百万次分配释放耗时约 80 毫秒我的内存池约 12 毫秒快了六倍多。这个差距主要来自通用分配器的查找和合并逻辑。多线程下差距更明显。系统分配器 4 线程耗时约 600 毫秒几乎是单线程的 7 倍多锁竞争导致性能严重劣化。我的内存池 4 线程耗时约 15 毫秒只比单线程略高说明线程本地缓存确实把锁竞争降到了很低的水平。场景系统分配器内存池提升倍数单线程 100 万次80ms12ms6.7x4 线程 100 万次600ms15ms40x这个数据让我确信在高频小对象场景下内存池带来的性能提升是数量级的。尤其是多线程场景通用分配器的锁竞争几乎让它不可用而内存池能保持接近线性的扩展性。5.3 参数调优的实操心得块大小档位的选择直接影响内存浪费率。我用 2 的幂次做档位最坏情况下浪费率接近 50%。比如请求 65 字节路由到 128 字节的池子浪费了 63 字节。如果对象大小分布集中在某些值附近可以针对性地设置档位把浪费率压下来。批量大小我试过 16、32、64、128。16 太小锁竞争还是明显128 太大本地缓存占用内存多而且线程退出时归还的块多退出开销大。64 是实测下来综合表现最好的。对齐边界我默认用 8 字节。如果你的对象里有double或者需要 SIMD 访问改成 16 或 32。但要注意对齐边界越大块大小向上取整后的浪费越多。提示调优时一定要用真实业务场景的数据不要用合成测试。合成测试的对象大小分布往往和真实场景差很远调出来的参数可能完全不适用。6. 常见问题与排查技巧实录6.1 内存泄漏怎么定位内存池的内存泄漏比普通泄漏更难查因为池子一次性申请大块内存泄漏的是块而不是系统内存。用常规的内存检测工具可能看不到异常因为池子本身的内存是正常持有的。我的排查方法是在池子里加统计计数。每个池子记录总共切了多少块、当前空闲多少块、已分配多少块。如果已分配块数持续增长不下降说明有泄漏。再配合在每个块头部记录分配时的调用栈调试模式下就能定位到泄漏点。struct BlockHeader { MemoryPool* pool; #ifdef DEBUG void* callstack[8]; int frames; #endif };调试模式下记录调用栈用backtrace之类的接口。生产模式下关掉避免性能开销。这个技巧帮我定位过好几次泄漏非常实用。6.2 线程退出时的崩溃问题前面提过thread_local变量在线程退出时析构如果此时内存池已经销毁归还块时就会访问已释放内存直接崩溃。我的解法是内存池不主动销毁。池子申请的内存一直持有到进程退出由操作系统统一回收。这样线程退出时归还块池子还在不会崩溃。代价是内存池占用的内存不会在运行期释放但对于长期运行的服务来说这点内存占用可以接受。如果确实需要销毁池子那必须确保所有线程都已退出或者在线程退出前主动清空本地缓存。这需要额外的同步机制复杂度高我一般不推荐。6.3 常见问题速查表问题现象可能原因排查方法解决方案分配返回空指针池子内存耗尽且扩容失败检查池子统计计数增大初始容量或处理分配失败多线程性能不升反降锁竞争严重用性能分析工具看锁等待时间增大批量大小或改用无锁队列程序随机崩溃块头部被越界写覆盖用内存检测工具检查越界加保护字节或改用带校验的头部内存占用持续增长内存泄漏看已分配块数是否持续增长定位泄漏点并修复对齐错误导致崩溃块大小未对齐检查对齐计算逻辑修正 alignUp 实现6.4 几个容易踩的坑第一个坑是忘记处理对齐。我一开始没做对齐测试时偶尔崩溃查了很久才发现是某些架构上未对齐访问会触发异常。加上对齐处理后问题消失。第二个坑是批量归还时的锁粒度。我最初在锁内遍历本地链表逐个归还锁持有时间随批量大小线性增长多线程下性能很差。改成先摘到临时链表再一次性拼接后性能明显改善。第三个坑是线程本地缓存的析构顺序。这个前面说过了解法是不销毁池子。但还有个变种问题如果池子是全局对象而线程本地缓存是另一个全局对象两者的析构顺序不确定也可能出问题。我的做法是把池子设计成永不析构的单例彻底规避这个问题。第四个坑是块头部的大小计算。头部占 8 字节但实际块大小要加上这 8 字节再对齐。我一开始忘了加导致块大小算错越界写破坏了相邻块。这个 bug 很隐蔽因为越界写不一定立即崩溃可能过很久才表现出来。7. 后续可以怎么扩展这套内存池目前只支持定长块如果对象大小变化大可以扩展成多档位自动路由申请时根据大小自动选池子对用户透明。这个扩展不难核心就是前面说的getPool逻辑。另一个方向是支持大对象。大对象走内存池不划算可以直接透传给系统分配器。判断标准可以设个阈值比如超过 4KB 的走系统分配器以下的走内存池。还可以加内存使用统计和监控把每个池子的使用率、分配次数、峰值等指标暴露出来方便线上监控和容量规划。这个在长期运行的服务里很有价值。我自己在实际使用中的体会是内存池这东西一旦用上就回不去了。尤其是高并发服务通用分配器的锁竞争是实打实的性能杀手而内存池能把这块开销几乎抹平。当然前提是场景匹配对象大小集中、分配频繁、生命周期短这三个条件满足得越多内存池的收益越大。如果对象大小五花八门、生命周期长短不一那还是老老实实用通用分配器别给自己找麻烦。

相关新闻

状态空间模型参数估计实战:从物理建模到嵌入式部署

状态空间模型参数估计实战:从物理建模到嵌入式部署

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 19:46:54 阅读更多 →
高职质检员转型品质管理的6大核心能力与实施路径

高职质检员转型品质管理的6大核心能力与实施路径

1. 高职学历质检专员转型品质管理分析的可行性路径我见过太多质检岗位的同事在职业发展上遇到瓶颈,特别是高职学历背景的同行。从重复性检测工作转向需要综合分析的品质管理岗位,确实存在知识结构和思维模式的差异。但根据我辅导过的37位成功转型案例来看…

2026/10/14 19:45:52 阅读更多 →
2026.10.13

2026.10.13

今天把第13章给看了一半多 感觉基础部分也不是很难 有些地方还是要多熟练下 明天就可以把听课部分给完结了今天英语记了百词斩五十词 和闪词过wordlist24

2026/10/14 19:45:52 阅读更多 →

最新新闻

权威测评!2026年最火AI论文工具榜单,免费款也能高效产初稿

权威测评!2026年最火AI论文工具榜单,免费款也能高效产初稿

一、实测方法论:5 大维度量化评估 本次实测覆盖全学科论文场景,重点考察以下维度,结果客观可复现: 表格 评估维度权重测评标准中文适配度25%是否符合 GB/T7714 引用规范,语言逻辑自然度,术语准确性免费功能…

2026/10/14 20:21:39 阅读更多 →
Linux嵌入式SD/MMC驱动调试:信号流向、三层架构与实战排错

Linux嵌入式SD/MMC驱动调试:信号流向、三层架构与实战排错

1. 项目概述:为什么一个SD/MMC驱动分析值得花时间深挖?在嵌入式Linux开发一线干了十多年,我经手过的板子从ARM9、ARM11到Cortex-A7/A53/A72,再到如今的RISC-V SoC,几乎每一块主控芯片的BSP包里,SD/MMC子系统…

2026/10/14 20:21:39 阅读更多 →
最小二乘法全攻略:从线性回归原理到代码实现与避坑实践

最小二乘法全攻略:从线性回归原理到代码实现与避坑实践

我第一次真正用上最小二乘法,是在处理一组传感器校准实验数据的时候。当时手里是一堆带噪声的采样点,教科书里的公式能帮我“找一条最合理的直线”,但工程上真正让人头疼的,其实是后面这些事:数据里混了离群点怎么办&a…

2026/10/14 20:21:39 阅读更多 →
大模型应用落地指南:从模型选型到Agent与RAG实践

大模型应用落地指南:从模型选型到Agent与RAG实践

1. 模型维度:基座能力的格局与选型逻辑距离大模型真正进入大众视野到现在,已经过了好几个年头。站在2026年10月这个时间窗口往回看,最明显的变化是:市面上能叫得出名字的大模型,已经从早期的"百家争鸣"收敛成…

2026/10/14 20:21:39 阅读更多 →
Linux MMC驱动深度解析:SD卡识别流程与SDIO协议栈实战

Linux MMC驱动深度解析:SD卡识别流程与SDIO协议栈实战

1. 项目概述:这不是教科书里的驱动理论,而是现场拆解一张SD卡如何被Linux真正“认出来” 你有没有遇到过这样的情况:插上一张SD卡,系统日志里刷出一串 mmc0: new high speed SDHC card at address 0001 ,但紧接着 d…

2026/10/14 20:21:39 阅读更多 →
从Zernike波前到PSF/MTF:光学像质仿真完整指南

从Zernike波前到PSF/MTF:光学像质仿真完整指南

做光学系统设计的人应该都有过这种体验:镜头草图画完、光线追迹也跑通了,但别人问"那成像到底清不清楚、能分辨多小的细节",你一时却拿不出一个直观的答复。光线追迹给的是光路,不是像质。真正用来回答"成像多锐利…

2026/10/14 20:20:38 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →