整理自 2023-09-04 群 telegram 上 Rust 众聊天记录,针对 Sync 与 Send 之间的讨论
理解
Sync
指的是允许 &T
暴露给多线程,一个类型支持 Sync
,可以看成这个类型看作 &T
Send
指的是允许 &mut T
或者 T
暴露给多线程,一个类型支持 Send
,可以把这个类型看作 &mut T
从 thread spawn capture 的角度来看为什么这么分类
compound type 内嵌的东西拆分出来,直接就相当于是一共三种: T, &mut T, &T, 三种分别约束一下是否允许带走。推理一下可知 T 和 &mut T 是否允许带走是一回事,所以只剩下 &T 不太一样,然后把这种情况放在 T 上单独取名叫 Sync。
——Carl Lei
Q1 Send 是否可以替代 Sync?
A1: 不能,Send 的作用类似 &mut T
但是他并不是真的就是 &mut T
。Send 的重点在 &mut T
能不能暴露给多线程,而不是 Send 代表 &mut T
。同理, Sync 的作用是指的 &T
能不能暴露给多线程,并不是 Sync 代表 &T
。
所以 T: Sync
的含义是,&T
可以在多线程之间暴露。所以 T: Sync
<=> &T: Send
。
Q2 为什么实现 Mutex Sync 需要 T Send
impl<T: ?Sized + Send> Sync for Mutex<T>
Mutex
是 Sync
的,相当于 Mutex
只要传递 &Mutex
,那为什么需要内部的 T
实现 Send
?
A2:
**Mutex 只是用来防 race, 不能违反其他安全约束的。**所以如果你想 Send &Mutex, 那因为你能从这得到 &mut T 所以就只能在 &mut T 也是 Send 的情况下才可以。然后 &mut T: Send 也就是 T: Send, 整个约束简化一下就是 Mutex: Sync where T: Send。——Carl Lei
但是我觉得上面这个说法虽然解释地十分精彩并简练,但是对新手来说不够详尽,所以做了一些补充。
Mutex<T>
存在一个 lock
方法,其返回的是 LockResult<MutexGuard<T>>
,其中重点是 MutexGuard<T>
,它实现了 DeferMut
的 trait。DeferMut
返回了 &mut T
。所以要求必须是 T: Send
。
Q3 为什么 MutexGuard 是!Send 的
A3:
MutexGuard
假如Send
的话,就代表&mut MutexGuard
,也就是 MutexGuard 可以在多线程之中被修改。而Mutex
的设计要求lock
与unlock()
必须在一个线程,所以是!Send
的。——布丁
Q4 解释为什么 Arc Send 需要 T Send + Sync
A4:
假设我是传给 thread spawn 的一个 closure, 我拥有一个 Arc:
- 我可以从 Deref 拿到一个 &T, 所以我得保证 &T: Send;
- 我 drop 这个 Arc 的时候可能执行到 T::drop 进而隐式的导致 &mut T 出现在我执行中的线程上,所以要保证 &mut T: Send.
还是引用的 Carl Lei 的回答。
还是针对新手在第二点上做一个补充,Arc 是通过 Arc::new(T)
而来,T
的所有权被传递到 Arc
中。当 Arc 被彻底 drop 的时候,会同时 drop 掉 T,所以需要 T 是&mut 的。
Q5 rc 为什么既不 Send 也不 Sync
首先它不 send 因为不是 atomic,引用计数可能出问题 ——布丁
sync 你拿一个&Rc 到别的 thread 去 clone 那不就 effectively 是 send 了,甚至不需要 只要是 clone 就会碰引用计数 就有可能 data race ——pop
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于