You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中Ordering::SeqCst内存序作用及案例疑问

Rust中Ordering::SeqCst的工作原理解析

一、计数器案例:Release/Acquire为何出现0结果,SeqCst为何能避免

Release/Acquire版本的0结果成因

Release和Acquire内存序仅保证特定线程对之间的单向同步:

  • Thread_a的a.store(true, Release)与Thread_1的a.load(Acquire)之间存在同步关系,Thread_1在看到a=true后,能确保Thread_a中所有在store之前的操作都已完成。
  • 但Thread_1后续的b.load(Relaxed)和Thread_b的b.store(true, Release)之间没有同步约束——CPU或编译器可能对操作重排序,或者缓存未及时同步,导致Thread_1看到a=true时,还看不到Thread_b写入的b=true。

同理,Thread_2看到b=true时,也可能看不到Thread_a写入的a=true。当这两种情况同时发生时,两个线程都不会执行counter.fetch_add,最终counter结果就是0。

SeqCst版本避免0结果的原因

SeqCst除了具备Release/Acquire的同步能力外,核心特性是所有标记为SeqCst的原子操作存在一个全局统一的单总修改顺序。也就是说:

  • Thread_a的a.store和Thread_b的b.store这两个SeqCst操作,在全局顺序里必然有先后之分:
    1. 若a.store先于b.store:当Thread_1看到a=true时,b.store已经在全局顺序中完成,Thread_1的b.load(SeqCst)必然能看到true,会执行counter加1;
    2. 若b.store先于a.store:Thread_2看到b=true时,a.store已经完成,Thread_2的a.load(SeqCst)必然能看到true,会执行counter加1。

无论哪种情况,至少有一个线程会对counter加1,因此0结果不可能出现。

二、字符串修改案例:Release/Acquire版本与SeqCst版本效果不一致

SeqCst版本的安全逻辑

原SeqCst版本中,A.store和B.store属于SeqCst操作,存在全局单总顺序:

  • 若A.store先执行:线程b加载A.load(SeqCst)会看到true,不会进入修改S的分支;线程a加载B.load(SeqCst)会看到false,进入修改分支。
  • 若B.store先执行:线程a加载B.load(SeqCst)会看到true,不修改S;线程b加载A.load(SeqCst)会看到false,进入修改分支。

两种情况都只会有一个线程修改S,不会出现并发修改的未定义行为。

Release/Acquire版本的风险

换成Release/Acquire后,失去了全局单总顺序的约束:

  • 线程a执行A.store(Release)后,线程b执行B.store(Release),此时两个store操作之间没有同步关系。
  • 线程a加载B.load(Acquire)时,可能还看不到B=true;同时线程b加载A.load(Acquire)时,也可能看不到A=true。
  • 这种情况下,两个线程都会进入修改S的分支,导致对S的并发写入——这属于未定义行为,可能引发内存损坏、程序崩溃等问题。

因此Release/Acquire版本无法保证和原SeqCst版本一致的安全效果。

三、理解SeqCst的“单总修改顺序”

“单总修改顺序”是指:所有标记为SeqCst的原子写操作(包括store、fetch_add等),在整个程序中存在一个唯一的、所有线程都认可的执行顺序;每个SeqCst读操作都会观察到这个顺序中最后一个对目标变量的写操作。

举个具体例子:
假设有两个原子变量:X: AtomicUsize = AtomicUsize::new(0)、Y: AtomicUsize = AtomicUsize::new(0),四个线程执行以下操作:

  • 线程1:X.store(1, SeqCst)
  • 线程2:Y.store(2, SeqCst)
  • 线程3:let x = X.load(SeqCst); let y = Y.load(SeqCst);
  • 线程4:let y = Y.load(SeqCst); let x = X.load(SeqCst);

在SeqCst约束下,全局顺序要么是X.store先于Y.store,要么是Y.store先于X.store:

  • 如果是X.store先:线程3会读到x=1, y=2,线程4也会读到y=2, x=1;
  • 如果是Y.store先:线程3会读到x=1, y=2,线程4同样会读到y=2, x=1。

绝不会出现“线程3读到X=1但Y=0,同时线程4读到Y=2但X=0”的情况——因为SeqCst强制所有线程对这些写操作的顺序看法完全一致。而如果用Release/Acquire,就可能出现这种“各说各话”的矛盾场景,因为它没有全局统一的顺序约束。

内容的提问来源于stack exchange,提问作者zexed640

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 20:20:56