Sync trait 是 Send trait 的严格子集吗?什么实现同步而不发送?

Eva*_*oll 4 asynchronous traits rust

Jim Blandy、Jason Orendorff、Leonora FS Tindall 的“Programming Rust,第 2 版”中第 520 页有一个图表显示了发送和同步,其中重叠圆圈中的同步完全包含在发送中。

图 19-9。 发送和同步类型

这让我相信所有实现同步的东西也必须实现发送,但是这个来自第 561 页的例子和我看到的所有东西总是分别指定它们,

type GenericError = Box<dyn std::error::Error + Send + Sync + 'static>
Run Code Online (Sandbox Code Playgroud)

为什么如果实现 Sync 的东西 100% 也是 Send,那么 Sync 不是 Send 的子特性吗?为什么 trait bound 需要同时指定两者?为什么人们都标记两者。有什么用例可以同步而不是发送吗?在什么情况下,您可以与另一个线程共享可变引用,但不能将所有权授予该另一个线程吗?

Ali*_*yhl 14

当类型实现该Send特征时,您可以执行以下操作:

  1. 从创建该值的线程以外的线程中可变地访问该值。这包括放弃它。
  2. 从创建该值的线程以外的线程中一成不变地访问该值。(但不一定是并行的。)

当类型实现该Sync特征时,您可以执行以下操作:

  1. 不可变地并行访问多个线程中的值。

另一种“定义” 含义的方法T: Sync就是简单地看它是否&T安全Send。这是有道理的,因为 an&T可以被复制,因此制作副本并将它们发送到不同的线程将允许并行不可变访问。


如果一个值是Send + !Sync,那么可以从任何线程以任何方式访问它,但一次只能从一个线程访问,即使访问是不可变的。

如果值是!Send + Sync,则可以从任何线程以及多个线程并行地访问它。但是,可变访问必须发生在创建它的线程上。


一些例子:

  • MutexGuard-MutexGuard在另一个线程上销毁 a 是不健全的,所以不可能Send。但是,如果可以从多个线程并行地不可变地访问内部的值,那么这种不可变的访问本身也是安全的MutexGuard
  • SyncWrapper- 对 a 的不可变引用SyncWrapper<T>根本不允许您执行任何操作,因此它始终是安全的Sync。(链接的板条箱要求内部值为Send,但这比必要的更严格)
  • &T- 由于可以复制不可变引用,因此将引用发送到另一个线程的能力将允许您并行地从多个线程执行不可变访问。因此&T只能是Sendif Tis Sync。没有必要,T因为Send不允许&T可变访问。
  • &mut T- 可变引用无法复制,因此将它们发送到其他线程不允许多个线程并行访问,因此即使不是,也&mut T可以是。当然,一定还是。SendTSyncTSend
  • Arc<T>- 这主要表现得像&T. 它可以被克隆,因此将其发送到其他线程需要T: Sync. 但是,它还需要T: Send因为最后一个Arc<T>可能会被删除到与创建的线程不同的线程上T,这是您无法做到的Send
  • RefCell<T>- 这种类型永远不可能,Sync因为您只能使用不可变引用修改内部的值,如果您可以从多个线程并行执行此操作,这将是数据竞争。RefCell<T>提供Send的话没有问题T
  • Rc<T>- 如果您有相同的两个克隆Rc<T>,那么从不同线程并行访问它们将是一场数据竞争。这排除了SendSync,因为它们都允许来自其他线程的不可变访问,并且其他线程可以使用它来远程调用并在另一个线程上.clone()获取。Rc<T>


Apl*_*123 8

这本书似乎是错误的。之间的唯一关系Send,并SyncTSync当且仅当&TSend(这是有道理的,因为“同步”跨线程其实只是能够在线程之间共享对它的引用)。事实上,标准库中甚至还有一种类型,Sync但不是Send: MutexGuard。原因是当尝试从锁定互斥锁的线程以外的线程解锁互斥锁时,底层实现会导致未定义的行为。