为什么`Arc <t>`要求T既是'Send`又是`Sync`才能成为`Send` /`Sync`?

时间:2017-01-28 12:30:46

标签: thread-safety rust

Arc<T> documentation说:

impl<T> Sync for Arc<T> where T: Send + Sync + ?Sized
impl<T> Send for Arc<T> where T: Send + Sync + ?Sized

Arc允许多个线程通过不可变引用T同时访问基础&T。只要T无法通过&T以不同步的方式修改,这就是安全的。对于具有“继承的可变性”(几乎所有类型)的所有类型都是如此,对于具有未同步的“内部可变性”(例如RefCell, ..)。

据我了解,此处不需要Send界限。例如,我认为Arc中共享my artificial type which implements Sync but not Send是安全的。

最后,&T本身也没有这个限制!在SendSync的文档中,我们找到了:

impl<'a, T> Send for &'a T where T: Sync + ?Sized
impl<'a, T> Sync for &'a T where T: Sync + ?Sized

由于Arc<T>允许T&T的访问次数相同,我不明白为什么Arc<T>Send的额外限制。 为什么?

1 个答案:

答案 0 :(得分:8)

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

考虑以下顺序:

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

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

&T并不需要Send,因为删除&T 永远可以删除基础值。

附录:作为一个存在问题的类型示例,请考虑类似struct Handle(usize);的类型,该类型由线程局部资源数组支持。如果这种类型的Drop实现在错误的线程上运行,这将导致它 执行越界访问(它试图销毁不具有&的资源) #39; t存在于此线程上),或者破坏仍在使用的资源。