内存屏障:给乱序执行立规矩
一句话为了快编译器会调换代码顺序CPU 会乱序执行写操作会先进发件箱而不是立刻送达。单线程看不出区别多线程之间就会出现旗子已经举起来数据还没到的怪事。内存屏障就是一道在此之前的事必须先办完的规矩。它不会让数据更快送达只保证别人看到的先后顺序和你想要的一致。前两篇讲无锁结构和原子操作时多次提到 acquire、release 和屏障。这一篇专门讲清楚三件事为什么会乱序、屏障到底做了什么、什么时候必须用它。一、先打个比方快递员和公告栏你是公司的行政要通知全员新的排班表已经贴在 3 号柜里。你的操作顺序是把排班表放进 3 号柜在群里发消息“排班表已更新去 3 号柜取”但你的快递员很高效送 3 号柜要绕远路群消息顺手就发了于是先发了群消息同事小李看到消息就去开柜子里面还是旧排班表小李那边也有问题他的桌上堆着一摞作废通知没来得及看柜子里其实已经换了可他手里拿的还是之前抄下来的旧副本办公室计算机你写下的步骤顺序程序代码的顺序快递员为了效率调换送件顺序编译器重排、CPU 乱序执行、写缓冲小李桌上没看的作废通知失效队列Invalidate Queue小李手里的旧副本缓存中过期的数据规定柜子没放好不准发群消息写屏障 / release规定看到群消息后先处理完作废通知再开柜子读屏障 / acquire内存屏障不是加速器而是顺序规矩。它告诉编译器和 CPU这条线前后的事情不许随意调换。二、谁在打乱顺序三个嫌疑人嫌疑人一编译器编译器只保证单线程下结果不变它完全可以调换、合并、甚至删除内存访问。最常见、也最吓人的例子classWorker{boolstop;// 普通字段publicvoidRun(){while(!stop)// Release 构建下可能变成if (!stop) while (true) {}{DoWork();// 若 DoWork 很简单、能被内联编译器更容易这样优化}}publicvoidStop()stoptrue;// 另一个线程调用}JIT 觉得循环里没人改stop就把读取提到循环外面结果另一个线程怎么改都停不下来。Debug 下正常Release 下死循环这是经典的只在正式包里出现的 Bug。volatileboolstop;// 方法一volatile 字段while(!Volatile.Read(refstop)){…}// 方法二每次都用 Volatile.Read嫌疑人二CPU 乱序执行现代 CPU 像一个高效的后厨前一道菜要等炖肉就先去炒后面的青菜。只要本线程看到的结果一致指令就可以不按顺序执行。嫌疑人三写缓冲和失效队列真正的硬件根源这一点要结合伪共享那篇讲过的 MESI 协议来看。写缓冲Store Buffer桌上的发件篮核心 1 要写变量 x x 所在缓存行在核心 2 那里 → 要先发请求等对方让出独占权几十到上百个周期 CPU 不想干等 → 先把x 1放进写缓冲继续往下执行 核心 1 自己读 x → 能从写缓冲里读到 1存储转发 核心 2 读 x → 写缓冲里的东西它看不到读到的还是旧值失效队列Invalidate Queue没拆的作废通知核心 1 通知核心 2你那份 x 作废了 核心 2 很忙 → 先回复收到把通知放进失效队列稍后再处理 在处理之前 → 核心 2 读 x读到的还是自己缓存里的旧副本硬件部件好处带来的乱序写缓冲写操作不用等缓存行到手写比后面的读晚到StoreLoad非先进先出的写缓冲ARM 等先拿到缓存行的先写写和写之间顺序颠倒StoreStore失效队列不用立刻处理作废通知读到过期数据LoadLoad乱序执行、推测执行不让流水线空转各种读写交错乱序不是 Bug而是性能优化的副作用。如果每次读写都老老实实排队CPU 的速度可能会掉到几分之一。所以硬件选择默认乱序需要时你来喊停喊停的方式就是内存屏障。三、四种重排两个内存操作之间按读Load和写Store组合有四种可能被调换的情况重排类型含义x86 / x64ARM / ARM64LoadLoad后面的读跑到前面的读之前❌ 不会✅ 会LoadStore后面的写跑到前面的读之前❌ 不会✅ 会StoreStore后面的写跑到前面的写之前❌ 不会✅ 会StoreLoad后面的读跑到前面的写之前✅会✅ 会x86TSO较强只允许 StoreLoad 重排 → 写缓冲导致 ARM较弱 四种都允许 → 写缓冲 失效队列 更激进的乱序这就是为什么同一份无锁代码在 PC 上跑几个月都没事一到手机上就偶发出错。PC 只是不容易出错不是写对了。四、两个经典实验亲眼看到乱序实验一发布数据StoreStore LoadLoadintdata0;boolreadyfalse;// 线程 A生产者data42;readytrue;// 线程 B消费者if(ready)Console.WriteLine(data);// 可能打印 0平台会不会出错原因x86硬件层面不会但编译器仍可能重排x86 不做 StoreStore、LoadLoad 重排ARM会ready可能比data先送达或者 B 先读了data的旧缓存实验二互相打招呼StoreLoad——连 x86 都会中招intx0,y0;intr1,r2;// 线程 A // 线程 Bx1;y1;r1y;r2x;直觉上至少有一个线程能看到对方的写入不可能出现r1 0 r2 0。但在x86 上也会出现线程 Ax 1 放进写缓冲 …… 读 y得到 0 线程 By 1 放进写缓冲 …… 读 x得到 0 两个写缓冲随后才清空可两人都已经读完了就像两个人同时写信给对方说我到了又同时去查信箱信都还在邮递员手里两人都以为对方没到。必须在写和读之间加全屏障x1;Thread.MemoryBarrier();// 先把写缓冲清空再往下读r1y;这个模式出现在 Dekker、Peterson 互斥算法以及先举手再看别人有没有举手的各种协议里。StoreLoad 是唯一需要重型屏障的情况。五、屏障的种类几种不同的关卡按约束方向分屏障约束比喻全屏障Full Fence前面的所有读写必须在后面的所有读写之前完成全员集合点之前的事全部办完才能出发写屏障Store Fence前面的写必须在后面的写之前可见先寄的信先到读屏障Load Fence前面的读必须在后面的读之前完成先拆完作废通知再看资料获取Acquire这次读之后的读写不能跑到它之前进场闸机进了门才能逛释放Release这次写之前的读写不能跑到它之后出场闸机东西都收好才能出门Acquire 和 Release两道单向门前面的操作可以往后漏进来 ↓ ─────────── acquire 读 ─────────── ← 后面的操作不许往前跑 ↑ 后面的操作被挡住 前面的操作被挡住 ↓ ─────────── release 写 ─────────── ← 前面的操作不许往后跑 ↑ 后面的操作可以往前漏进来单向是关键它们只挡一个方向比全屏障便宜很多而且成对使用刚好够用线程 A 线程 B 写 data 写 data2 release 写 ready true ── 同步 ──→ acquire 读 ready true 读 data ← 一定能看到 42 读 data2记住这个画面release 是打包封箱acquire 是签收开箱。一方封箱、另一方签收箱子里的东西就一定完整。按作用对象分类型作用例子编译器屏障只阻止编译器重排不生成 CPU 指令Cstd::atomic_signal_fence、GCCasm volatile( ::: memory)CPU 内存屏障同时约束编译器和 CPUmfence、dmb ish设备 / DMA 屏障约束 CPU 和外设之间的顺序驱动程序中写设备寄存器前的dsb指令屏障清空流水线让新写入的代码生效ARM 的isbJIT 生成代码后需要六、屏障在硬件上到底做了什么屏障不是把缓存刷到内存而是处理那两个导致乱序的部件屏障硬件动作比喻写屏障后面的写要等写缓冲中前面的写完成或打上标记按顺序提交发件篮里的信寄完新信才能寄读屏障先处理完失效队列再执行后面的读先看完作废通知再翻资料全屏障清空写缓冲 处理失效队列 阻止后续读写提前发件篮清空、通知看完、才继续工作没有屏障 核心 1写 x → [写缓冲x1] → 读 y直接执行不等 有全屏障 核心 1写 x → [写缓冲x1] → ███ 等待写缓冲清空 ███ → 读 y不同平台上的具体指令语义x86 / x64ARM64全屏障mfence或带LOCK前缀的指令常用lock or [rsp], 0dmb ish写-写屏障通常不需要普通写本来就有序dmb ishst读屏障通常不需要dmb ishldacquire 读普通movldar或ldaprrelease 写普通movstlr原子读-改-写lock xadd、lock cmpxchg自带全屏障ldaddal、casal或 LL/SC 配合两个值得注意的现象x86 上 acquire 和 release 几乎免费只需要阻止编译器重排所以很多代码在 PC 上不加也能跑x86 上lfence/sfence主要用于特殊场景非临时写、推测执行防护等普通多线程同步基本不用。七、代价屏障也不是免费的屏障大致代价编译器屏障不生成指令但可能阻止一些优化x86 acquire / release几乎为零ARMldar/stlr比普通读写稍慢全屏障mfence、dmb ish几十到上百个周期写缓冲越满越慢带锁的原子指令同上并且竞争时还有缓存行传递成本所以原则是够用就好。需要发布 / 订阅 → release 写 acquire 读 ← 大多数情况 需要先写再读别人 → 全屏障 ← 少数情况StoreLoad 不确定 → 用锁或 Interlocked ← 慢一点但不会错八、具体作用屏障在哪些地方默默工作作用一安全地发布数据usingSystem.Threading;classSnapshot{intdata;boolready;publicvoidPublish(intvalue){datavalue;Volatile.Write(refready,true);// releasedata 先于 ready 可见}publicboolTryRead(outintvalue){if(Volatile.Read(refready))// acquire看到 ready 就一定能看到 data{valuedata;returntrue;}value0;returnfalse;}}无锁那篇的 SPSC 环形队列、原子替换配置引用的热更新都是这个模式。作用二锁本身就包含屏障很多人不知道锁之所以能保护数据不只是因为互斥还因为它自带屏障。lock(obj) { ← 加锁 acquire之后读到的一定是上一个持锁者写的最新值 count; total x; } ← 解锁 release这里面的所有写一定在下一个人进来前可见如果锁只做到互斥而没有屏障下一个线程进来时可能看到的还是旧的count。所以用锁的代码不需要再手动加屏障。作用三双重检查锁定单例classAssetCache{staticvolatileAssetCacheinstance;// 关键在这个 volatilestaticreadonlyobjectgatenewobject();publicstaticAssetCacheInstance{get{varinstinstance;// acquire 读if(inst!null)returninst;lock(gate){if(instancenull)instancenewAssetCache();// release 写returninstance;}}}readonlyDictionarystring,objecttablenew();}如果没有屏障instance new AssetCache()可能被拆成① 分配内存 ② 把地址写进 instance ← 被提前了 ③ 执行构造函数初始化 table另一个线程在 ② 和 ③ 之间看到instance ! null拿到一个还没构造完的对象访问table时直接出错。实际项目中更推荐直接使用LazyT或静态只读字段初始化让运行时替你处理。作用四互相等待的协议必须用全屏障// 两个线程都想进入临界区先举手再看对方有没有举手volatileboolwantA,wantB;// 线程 AwantAtrue;Thread.MemoryBarrier();// 必须是全屏障防止读 wantB跑到写 wantA之前if(!wantB){/* 进入 */}这里 acquire / release 不够用因为要阻止的正是StoreLoad重排。作用五Unity 中的隐式屏障场景屏障在哪里JobHandle.Complete()后主线程读取NativeArray调度器内部的同步已经包含屏障Job 之间通过依赖链传递数据前一个 Job 结束、后一个开始时自动同步主线程 → 音频线程的命令队列需要自己处理用Volatile或Interlocked后台加载线程 → 主线程的完成标志需要自己处理ConcurrentQueue等并发容器容器内部已处理一个好的习惯线程之间只通过自带屏障的东西交换数据比如锁、并发容器、Job 依赖、Volatile/Interlocked。普通字段一律不跨线程共享。作用六CPU 与外部设备之间屏障的思想不止存在于 CPU 核心之间场景屏障的作用驱动程序写设备寄存器先把数据写入共享内存再按门铃通知设备CPU 与 GPU 共享缓冲区先写完数据再提交命令GPU 内部Vulkan / D3D12 的 Barrier先写完纹理再允许下一个阶段读取JIT 生成机器码写完代码后需要指令屏障CPU 才会执行新代码名字一样思想一样“之前的写必须在之后的读之前完成。”九、各语言里的屏障工具箱C#工具语义Volatile.Readacquire 读Volatile.Writerelease 写volatile字段读为 acquire写为 releaseThread.MemoryBarrier()/Interlocked.MemoryBarrier()全屏障Interlocked.*原子操作 全屏障lock/Monitor进入为 acquire退出为 release⚠️ C# 的volatile不能阻止 StoreLoad 重排一次 volatile 写后紧跟一次 volatile 读仍然可能被调换。需要时用Thread.MemoryBarrier()。C#includeatomicstd::atomicboolready{false};intdata0;// 生产者data42;ready.store(true,std::memory_order_release);// 消费者if(ready.load(std::memory_order_acquire))use(data);// 独立的屏障std::atomic_thread_fence(std::memory_order_seq_cst);// 全屏障std::atomic_signal_fence(std::memory_order_seq_cst);// 仅编译器屏障C 内存顺序作用relaxed只保证原子性不约束顺序acquire读操作之后的不许往前release写操作之前的不许往后acq_rel读-改-写操作两边都管seq_cst最强所有线程看到同一个全局顺序默认值⚠️ C 的volatile和 C# 的完全不同它既不保证原子性也不保证线程间顺序主要用于硬件寄存器。多线程同步请用std::atomic。十、常见误解误解实际情况“屏障会把数据立刻刷到内存”屏障约束的是顺序不是速度缓存一致性协议本来就保证数据最终可见“加了屏障别的线程马上就能看到”屏障只保证如果看到了 B就一定能看到 A不保证什么时候看到“只在写的一方加屏障就够了”屏障要成对发布方 release读取方 acquire缺一不可“屏障能让i变成原子的”屏障管顺序不管原子性上一篇的原子管插队屏障管顺序“x86 上测过没问题就是对的”x86 内存模型强掩盖了大部分问题到 ARM 设备上才会暴露“屏障越多越安全”过多的全屏障会明显拖慢性能应该按需选择最弱且足够的语义“单线程程序也需要屏障”单线程永远看到正确的顺序只有跨线程或跨设备才需要十一、调试为什么这类 Bug 这么难抓难点原因极难复现需要特定的时序、特定的缓存状态可能百万次才出现一次加日志就消失日志本身通常带锁和屏障改变了时序“海森堡 Bug”PC 上不出现x86 内存模型强Debug 版不出现编译器优化少应对方法方法说明在 ARM 设备上做压力测试手机、Apple M 系列 Mac使用 Release 构建让编译器优化生效工具检测C 用 ThreadSanitizer形式化验证可以用 CDSChecker、herd7 等 litmus 测试工具代码审查时问一句“这个字段跨线程了吗它靠什么保证顺序”最根本的办法少写自定义无锁代码优先使用成熟的并发容器和 Job 系统一句话总结为了快编译器会调换顺序CPU 会乱序执行写操作会先进发件篮作废通知会先堆在桌上。单线程看不出任何问题跨线程就会看到旗子举起来了数据还没到。内存屏障就是那条之前的事必须先办完的规矩。问题回答谁在打乱顺序编译器、CPU 乱序执行、写缓冲、失效队列屏障管什么顺序不管速度也不管原子性最常用的组合发布方release 写读取方acquire 读成对使用什么时候要全屏障先写自己的、再读别人的StoreLoad比如举手再看别人举没举手x86 和 ARM 的区别x86 只会 StoreLoad 重排ARM 四种都会PC 上没问题不代表手机上没问题哪些东西自带屏障锁、Interlocked、Volatile、并发容器、Job 依赖、JobHandle.Complete()最安全的做法普通字段不跨线程共享跨线程只通过自带屏障的工具交换数据口诀“先封箱再贴条release先签收再开箱acquire先写后读要全停full fence屏障管顺序不管快成对出现才算数PC 跑通不算对手机上面见真章。”附三篇连起来看前三篇其实是同一件事的三个侧面┌──────────────────────────────────────────────────────────┐ │ 无锁数据结构 用 CAS 循环、序号、互相帮忙搭出不阻塞的容器 │ │ ↑ 依赖下面两样 │ ├──────────────────────────────────────────────────────────┤ │ 原子操作 / 原子指令 保证一个变量的修改不会被插队 │ │ 原子管插队 │ ├──────────────────────────────────────────────────────────┤ │ 内存屏障 保证多个变量的修改别人按正确顺序看到 │ │ 屏障管顺序 │ ├──────────────────────────────────────────────────────────┤ │ 缓存一致性MESI 保证同一个缓存行最终大家看到的是同一个值 │ │ 写缓冲、失效队列是它的性能优化也是乱序的根源 │ └──────────────────────────────────────────────────────────┘只做到会出现的问题有原子性没有屏障计数器是对的但数据和标志之间顺序错乱有屏障没有原子性顺序是对的但count仍然会丢更新两者都有但设计上竞争激烈正确但缓存行乒乓比锁还慢两者都有竞争也少✅ 这才是好的并发代码

相关新闻

AI编码代理实战:从任务拆解到工程交付的行动营复盘

AI编码代理实战:从任务拆解到工程交付的行动营复盘

1. 项目概述与核心思路拆解1.1 这个项目到底在做一件什么事先说结论:银河it 2026年Codex AI工程交付行动营,本质上是一个以真实工程项目交付为靶场、以Codex类AI编码代理为核心生产力工具的集中训练项目。它不教你“AI能写代码”这种概念,也不…

2026/10/18 2:40:02 阅读更多 →
原子操作与原子指令:一个承诺,多种实现

原子操作与原子指令:一个承诺,多种实现

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

2026/10/18 2:40:02 阅读更多 →
基于5777张图片的垃圾检测数据集实战:六大场景与模型训练指南

基于5777张图片的垃圾检测数据集实战:六大场景与模型训练指南

垃圾检测在计算机视觉里是个看着简单、做起来相当磨人的细分方向。说它简单,是因为目标就是“找出画面里的垃圾”;说它磨人,是因为垃圾这东西没有固定形状、没有统一颜色、尺度跨度极大——一个矿泉水瓶和一个建筑垃圾堆,在特征层…

2026/10/18 2:39:02 阅读更多 →

最新新闻

自然语言驱动图表生成深度实战:图表狐在气候、能源等5大场景下的文本指令控制范式

自然语言驱动图表生成深度实战:图表狐在气候、能源等5大场景下的文本指令控制范式

传统图表工具需要手动操作或编写代码,而图表狐(https://graphai.crazystone.work)基于AIGC技术,让用户通过自然语言即可生成折线图、桑基图、雷达图等28种专业图表。本文通过5个跨领域实战案例(气候、运动、考古、能源…

2026/10/18 3:29:17 阅读更多 →
AIME竞赛是什么?3小时15道整数难题背后的数学背景与aime_2026数据集渊源

AIME竞赛是什么?3小时15道整数难题背后的数学背景与aime_2026数据集渊源

【免费下载链接】aime_2026 项目地址: https://ai.gitcode.com/hf_mirrors/MathArena/aime_2026 点击查看 免费下载 AIME竞赛(American Invitational Mathematics Examination,美国数学邀请赛)是全球最著名的中学生数学竞赛之一&…

2026/10/18 3:29:17 阅读更多 →
openEuler 24.03 LTS SP3安装实战:从U盘启动到系统初始化避坑指南

openEuler 24.03 LTS SP3安装实战:从U盘启动到系统初始化避坑指南

装系统这件事,在运维日常里看起来是最基础的操作,真上手了才明白坑有多深。前几天给一台业务服务器换装 openEuler 24.03 LTS SP3,从下载镜像到重启进系统,前后折腾了整整一个下午,踩了引导识别失败、老磁盘分区表残留…

2026/10/18 3:29:17 阅读更多 →
Codex 一键安装包实操分享:把 auth.json 改到 TaoToken 后几分钟完成部署

Codex 一键安装包实操分享:把 auth.json 改到 TaoToken 后几分钟完成部署

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

2026/10/18 3:29:17 阅读更多 →
从零搭建轻量文章管理系统:Node.js与SQLite实战解析

从零搭建轻量文章管理系统:Node.js与SQLite实战解析

1. 项目概述与设计初衷1.1 这个标题到底说的是什么“weixin105文章管理系统”,第一眼看上去像是个内部项目代号,但拆开来看,其实是一个非常典型的内容管理需求:一套面向个人或小团队的公众号文章管理方案。weixin105这个编号&…

2026/10/18 3:29:17 阅读更多 →
ADAMS/View高级培训光盘实战:多体动力学建模、求解与参数化

ADAMS/View高级培训光盘实战:多体动力学建模、求解与参数化

简介:面向ADAMS/View中高级用户的官方培训光盘资源,适合机械、车辆及航空航天领域需要提升多体动力学仿真能力的工程师。资源包内含477个文件,大小仅4.75MB,文件类型以cmd命令脚本、jpg/htm图文资料、txt说明文本和gif动画演示为主…

2026/10/18 3:28:16 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 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/18 0:55:02 阅读更多 →
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/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练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/16 14:23:36 阅读更多 →