为什么`Rc` 线程不安全?

Gue*_*OCs 2 multithreading rust

我在读第16章-共享国家并发锈病程序设计语言。它说:

不幸的是,Rc<T>跨线程共享是不安全的。当Rc<T> 管理引用计数时,它会添加到每次调用 clone 时的计数,并在删除每个克隆时从计数中减去。但它不使用任何并发原语来确保对计数的更改不会被另一个线程中断。

这是什么意思

确保对计数的更改不会被另一个线程中断。

我认为计数更改被中断的唯一情况是如果创建了一个线程,并且它以某种方式发生恐慌/崩溃,因此锁柜永远不会解锁,因此引用计数永远不会减少。我可以想象如果发生恐慌,Rust 会调用作用域中每个对象的析构函数。

有人可以为我澄清这一点吗?

Mat*_* M. 10

确保对计数的更改不会被另一个线程中断。

这是一个非常不幸的措辞,而且是不准确的。真的,中断是我们最不担心的。

就并发而言,Rust 的内存模型是基于 C11 和 C++11 采用的内存模型。如果你想了解更多关于记忆模型的知识,我只推荐阅读 Preshing 关于Weak vs Strong memory models的文章;我会尽量在这个答案中做到实质性的公正。

你问什么是内存模型?

粗略地说,内存模型是一个模型,它指定哪些操作可以重新排序,哪些不能。

可能会发生重新排序:

  • 在优化器中。
  • 在 CPU 中。

一般来说,出于性能原因,重新排序读/写是好的。它可以更有效地利用 CPU 并加快进度。但是,某些算法的正确性取决于各种线程以特定顺序观察事件……因此有时不应重新排序某些读/写。内存模型和内存顺序用于指定编译器和 CPU 正确执行算法应遵守的确切约束。

CPU怎么会坏Rc

通过忽略增量。

在弱内存模型中,如果两个不同的内核增加计数器,则其中一个增量可能会被忽略。

想象以下时间线,在给定线程上,其中 CN 表示当前所有者的数量为 N,而 C0 表示销毁。

 T1 -- Create: C1 --- Clone: C2 -- Drop Clone: C1 --- Drop: C0.
Run Code Online (Sandbox Code Playgroud)

现在,想象一下这个线程共享Rc

 T1 -- Create: C1 --- Clone: C2 ---------------C1---- Drop Clone: C0 --- Access **BOOM**.
                  \                                 /
 T2                \_ Clone: C2 -- Drop Clone: C1 _/
                              ^                 ^
    Only one increment was seen                 But both decrements are
Run Code Online (Sandbox Code Playgroud)

为什么 CPU 会这样做?

表现。

强大的内存模型意味着内核之间有很多不必要的抖动来同步缓存行——抖动会增加操作的延迟。

较弱的内存模型允许较少的抖动,从而减少延迟,这意味着程序可以执行得更快或功耗更低。

如果内存模型足够强大?

即使在每个读/写都涉及内存的假设 CPU 上,由于竞争条件,它仍然可能出错。

具体来说:

  • T1 读取计数 (1),T1 计算递增计数 2,T1 写入计数 (2)。
  • T2 读取计数 (1),T2 计算递增计数 2,T2 写入计数 (2)。

如果你看一下AtomicXXX在鲁斯特的类型,你会发现一些RMW(读-修改-写)操作,如存在fetch_add这些原子读,增量,和写。

原子性很重要,否则可能会出现竞争条件。

优化器怎么会坏Rc

即使在没有任何寄存器的假设 CPU 上,增量/减量将直接以原子方式修改内存,事情仍然可能出错。

在没有内存排序的情况下,优化器可以假设没有其他执行线程正在观察对内存的写入:毕竟这样做是未定义的行为。

因此,优化器完全可以:

  1. 创建Rc.
  2. 放下原来的。
  3. 递减计数器 (-2) -- 融合递减以获得乐趣和利润!
  4. 使用克隆。
  5. 增加计数器 (+1)。
  6. 放下克隆。

如果另一个线程丢弃 (3) 和 (5) 之间的最后一个其他引用,计数器将达到 0,因此另一个线程将丢弃里面的值。

我不确定我理解...

别担心,你没有必要

Rust 编译器支持你。除非你抛出unsafe,否则它会确保你不会意外地引入这样的竞争条件。

至于理解这一切,那里有很多文献。订购的确切效果已记录在案,为了更大的图景,Preshing 真的很好,我衷心推荐他们的博客。