加载失败
讨论源自对在异步代码中使用 async mutex 来保护 compaction 等回调原子性的质疑,原文建议把整个 compaction 包裹在互斥锁并在回调一开始 lock/unlock。评论集中在单线程协作式调度(例如 JS 的 event loop 与 await)语义:在遇到 await 前代码是原子的,设计上应在让出前保持或重建一致性而非依赖互斥。讨论还对比了 actor 模型(Erlang/GenServer/BEAM)及基于 async queue 的序列化替代方案,并提出了用读锁+原子替换在 compaction 场景下避免全局锁的具体做法。最后有评论把问题提升到进度保证层面,问是否应采用 wait-free/非阻塞算法或语言层面的内存隔离(如 Rust)来从根本上避免共享堆危险。
评论指出在单线程、协作式调度(例如 JavaScript 的 event loop 与 await 语义)里,每次执行在遇到 await 前实际上是“原子”的;正确做法是确保在让出(await)前将状态保持一致,或在块开始时重建状态以避免陈旧捕获。把 async mutex 用来修补这种原子性缺失等同于承认“不知道何时会 await”,这被看作设计者不熟练的标志,而且会引入难以排查的 bug、死锁和事件队列调度延迟。评论还提到 TypeScript 的类型缩小(type narrowing)不会因为调用 async 函数而自动失效,这使人更容易用锁掩盖逻辑错误而非修正设计。总体结论是:在这类环境中应优先通过设计保证原子性,而非依赖 async mutex。
有评论把讨论拉到 actor 模型:标准的 actor(例如 Erlang/GenServer 在 BEAM VM 上的实现)遵循 shared-nothing、消息序列化和单进程串行处理,因此不会在回调中被中断并产生恢复时的陈旧状态问题。许多系统(评论中提到 TigerBeetle 用 Zig、FoundationDB 用 C++)在工程上演化出 actor 式模式,但常常缺失隔离堆或正确的让出语义,从而无法真正规避共享堆带来的危险。评论者认为真正的解决方向是保证进程间隔离或在语言层面(例如 Rust 的编译时保证)引入更强的所有权/内存隔离,而不是在共享堆上叠加 async mutex。
评论提出具体替代策略:学习并区分 push vs pull reactivity,并用 async queue 将反应序列化(pull reactivity)可以在很多场景代替互斥锁,使逻辑更可预测。针对文章提到的 compaction 场景,有人建议在生成 C 时对被合并的对象 A、B 先取读锁以便仍能被查询,待 C 生成后再以原子操作添加 C 并移除 A 与 B,从而避免出现重复或丢失记录,并避免把所有回调包在单一全局锁里造成瓶颈或死锁(“dining philosophers”)。还有实用疑问,例如如何在树结构上异步遍历且可能被编辑时保持一致性——这些场景通常更适合序列化更新、版本号或队列化操作而非简单的 async mutex。
有评论直接提出进度保证的问题:既然方案仍依赖锁,那它就不是 wait-free,锁在异步/单线程环境下仍会造成“看起来像阻塞”的调度延迟和死锁风险。非阻塞或 wait-free 算法在理论上能提供更强的进度和响应性保证,但实现复杂且并非对所有问题都实用。讨论围绕正确性(避免竞态)、性能(事件队列调度开销)与实现复杂度之间的折衷展开,并暗示在许多实际场景中更合适的做法是序列化操作或使用 actor/队列而非盲目加锁。
async mutex (异步互斥锁): 在异步/协作式环境中用于序列化对共享状态访问的锁,通常以 Promise/await 形式工作;在单线程事件循环(如 JavaScript)中会在 await 点让出执行,从而引入事件队列调度延迟并可能导致复杂的死锁与调试难题。
actor model (Actor 模型): 以消息传递、shared‑nothing(隔离堆)和每个 actor 串行处理消息为特征的并发模型;通过序列化消息处理避免回调中被打断导致的陈旧状态问题。
GenServer: Erlang/Elixir OTP 中的通用服务器行为(GenServer),常用于在单一进程内序列化管理状态和消息处理,代表性地体现了 actor/消息队列风格的设计。
BEAM VM: Erlang 的运行时虚拟机(BEAM),以大量轻量进程、隔离堆和可靠的消息调度著称,能天然避免共享堆导致的许多竞态问题。
wait-free / non-blocking algorithm (无等待/非阻塞算法): 不依赖传统锁的算法范式;wait-free 指每个参与者在有限步骤内保证完成操作,提供强进度保证,但通常实现更复杂。
pull reactivity / async queue (拉式响应/异步队列): 通过将更新作为序列化任务入队并由消费者顺序执行来处理状态变化的模式;相比互斥锁,这种序列化反应能降低竞态和调度复杂度。