为什么`Arc <T>`要求T同时为'Send`和`Sync`才能成为`Send` /`Sync`?

Luk*_*odt 6 thread-safety rust

Arc<T>文件说:

impl<T> Sync for Arc<T> where T: Send + Sync + ?Sized
impl<T> Send for Arc<T> where T: Send + Sync + ?Sized
Run Code Online (Sandbox Code Playgroud)

一个Arc允许多个线程同时访问底层T经由一个不可变的参考&T.只要T不能以非同步方式修改,这是安全的&T.对于具有" 继承可变性 "(几乎所有类型)的所有类型都是如此,对于具有不同步的" 内部可变性 "(例如RefCell,......)的类型则是如此.

据我了解,Send此处不需要绑定.举例来说,我觉得分享我的一个实现人工型Sync,但不是Send在一个Arc很安全.

最后,&T本身也没有这个约束!在对文档SendSync我们发现:

impl<'a, T> Send for &'a T where T: Sync + ?Sized
impl<'a, T> Sync for &'a T where T: Sync + ?Sized
Run Code Online (Sandbox Code Playgroud)

而作为Arc<T>允许相同的访问T作为&T呢,我不明白为什么Arc<T>有更多的Send约束.这是为什么?

DK.*_*DK. 8

我相信这是因为它Arc 拥有它包含的值,因此负责删除它.

请考虑以下顺序:

  • 类型的值T在线程1中创建它不是Send,这意味着它是不是安全的,这个值移动到另一个线程.
  • 此值将移动到Arc句柄中.
  • 句柄的克隆被发送到线程2.
  • 线程1存储的句柄被丢弃.
  • 线程2存储的句柄被丢弃.由于这是最后一个句柄,因此它假定存储值的完全所有权并将其删除.

就像那样,我们将类型的值T从一个线程移动到另一个线程,这违反了内存安全性.

&T不需要Send因为删除&T 永远不允许你删除基础值.

附录:作为一个存在问题的类型的示例,请考虑类似于struct Handle(usize);线程局部资源数组的类型.如果Drop这种类型的实现是错误的线程上运行,这将导致它要么做一个彻头彻尾的越界访问(它试图摧毁并不在此线程存在的资源),或破坏资源那还在使用中.

  • @LukasKalbertodt我不认为具体的例子在实践中会成为一个问题.*在我的脑海中,像处理到线程本地存储的东西,或只能从特定线程使用的OS值会浮现在脑海中.想想像`struct Handle(usize);`那样索引到一个线程本地的资源数组; 将它放在错误的线程上会导致越界访问或删除错误的资源. (2认同)