Rust Atomics and Locks中,建议使用以下代码来适当地实现简化的Arcdrop特性:(代码是我的)

        unsafe {
            if 1 == (*self.inner).strong.fetch_sub(1, Release) {
                fence(Acquire);
                let box_ = Box::rom_raw(self.inner);
                drop(box_);
            }
        }

其中(*self.inner).strongAtomicUsize.

所以我的问题是:

我理解acquire将与前一个release(与写入加载值的线程)建立"happens-before/synchronize-with"关系.为了正确地drop值,我们需要每个Arc都有他们的fetch_sub一个接一个地链接.

我不明白这里怎么能保证这一点.我可以理解,如果我们在一次操作中完成acquired个原子值,减go 1,然后是released(基本上是fetch_sub次排序或一个比较和交换循环).

在这里,我们只有在加载1的时候才"获取".我对fetch_sub使用Release的理解是fetch部分是relaxed.基于这种理解,我对上述代码有两个问题:

1

这个案子是不是有可能:

Thread A:
  load: 2. (t = 1)
  write 1 (t = ?)
Thread B:
  load: 2. (t = 1)
  write 1 (t = ?)

因此价值从未下降?我看不出load如何保证它对值具有独占访问权,这意味着它将始终按顺序加载.

2.

我难道不是在1的读数和下降之间建立了一种"之前发生"的关系吗?我认为这很好,因为如果我真的看到1,我可以下降only,但如何保证我看到1,而不会在原子上进行数据竞赛?

我怀疑我的误解来自于原子/排序提供的保证,但是

推荐答案

原子读-修改-写指令始终是原子指令,与所选的内存顺序无关.因此,如果假设self.inner的值为5,然后在不同线程的任意组合中执行fetch_sub(1, Relaxed),您仍然可以保证其中恰好有一个线程将加载值2并存储值1.

Rust没有正式定义的内存模型,但AFAIK在实践中遵循C++,它通过说"原子读取-修改-写入总是加载值,在对象的修改顺序中,紧接在它存储的值之前."一百

在1的加载和删除之间确实存在发生之前的关系,但这是微不足道的;对于单个线程中的操作,发生之前总是遵循程序顺序.令人困惑的是,这并不意味着它们不能被重新排序以供执行!

你真正关心的是,来自其他线程的all prior accessesbox_必须在删除之前发生.这是避免数据竞赛的条件.多亏了获取和释放排序的使用,你确实得到了这一点.

如果fetch_sub个人使用AcqRel顺序,那就更简单了.在这种情况下,我们将:

  • 最后一次之前的访问发生在值1的存储之前(因为它在单个线程内的程序顺序中先于fetch_sub)

  • 1的存储与1的加载同步,因为前者是释放的,后者是获取的,并且加载观察到的值是存储放在那里的值

  • 1的加载发生在丢弃之前(程序顺序)

  • 发生-之前是及物性的.

您可以为所有其他访问框的权限进行类似的争辩.

但是使用AcqRel有点低效,因为我们为每fetch_sub次的获取排序付费,但我们实际上只需要为最后一次(加载值为1的那次)进行排序.因此,在这段代码中,我们只在fetch_sub上使用Release排序,并且只在加载的值等于1时才有条件地执行Acquire限制.该限制可以被认为是"追溯"地促进fetch_sub的轻松加载来获取.正式的证明需要更多的工作,但它确实奏效了.

还有另一个附带的问题:如果只订购Release个订单,您就不再有一个包含所有fetch_subs个订单的完整的先发生链,那么如何保证drop会在倒数第二次访问之后发生?C++内存模型使用"释放序列"的概念来处理这一问题.通常,加载只有在加载存储放入的值的情况下才能与存储同步.但是释放序列意味着,如果您加载的值是通过对最初存储的值进行某种原子读-修改-写操作而生成的,那么它也是"算数"的.所以你的下降不仅发生在值1的存储之后,它也发生在假想的值47存储之后(因为我们只能通过一些长的原子fetch_addfetch_sub链达到1).

Rust相关问答推荐

两个相关特征的冲突实现错误

如何正确地将App handler传递给Tauri中的其他模块?

如何计算迭代器适配器链中过滤的元素的数量

无法定义名为&new&的关联函数,该函数的第一个参数不是self

`actix-web` 使用提供的 `tokio` 运行时有何用途?

Rust proc_macro 和 syn:解析空格

Rust,如何从 Rc> 复制内部值并返回它?

可选包装枚举的反序列化

面临意外的未对齐指针取消引用:地址必须是 0x8 的倍数,但为 0x__错误

为什么 Rust 的临时值有时有参考性有时没有?

一个函数调用会产生双重borrow 错误,而另一个则不会

Rust Serde 为 Option:: 创建反序列化器

我的 Axum 处理程序无法编译:未实现 IntoResponse 特征

没有分号的返回表达式的性能是否比使用返回更好?在Rust ?

If let expression within .iter().any

仅当满足外部条件时如何添加到 actix web 的路由

Cargo:如何将整个目录或文件包含在功能标志中?

使用泛型作为关联类型,没有幻像数据

为什么我不能为 Display+Debug 的泛型类型实现 std::error::Error 但有一个不是泛型参数的类型?

Rust 为什么 (u32, u32) 的枚举变体的大小小于 (u64)?