我搜索的类型是Sync
,但不是Send
,因为它通常看起来一个特征是另一个特征的超集(“实现Sync
的每种类型也实现Send
”)。我找到了this question,但是唯一的真实答案是非常复杂的。
所以我想出了这段代码:
struct Foo(Rc<()>); // <-- private field
impl Foo {
fn my_clone(&mut self) -> Self { // <-- mutable borrow
Foo(self.0.clone())
}
}
我知道编译器不会为我的类型自动实现
Send
或Sync
;但是我对我可以安全地手动实现的功能感兴趣。我认为:Sync
:对Foo
的引用是不可变的,因此我们无法对其进行任何处理(因为我们只能通过可变/专有引用来调用my_clone()
)。如果不做任何事情,什么都不会出错,对吗? Send
:我们可以在主线程中克隆Foo
(在启动另一个线程之前)以获取第二个对象。现在,两个对象共享一些内存(引用计数,存储在Cell<usize>
中)。如果现在我可以将这些对象之一发送到另一个线程,则两个线程都将拥有Foo
的所有权,并引用相同的内存。因此,两个对象可以同时调用my_clone()
,从而导致对引用计数的同时,不同步,可变访问(数据竞争)。 这个推理是正确的还是我错过了什么?
最佳答案
实际上,只有在编译器确定可以安全地执行此操作时,编译器才会自动为您实现Send
和Sync
。
这个小程序:
use std::cell::Cell;
use std::sync::atomic::AtomicUsize;
fn ensure_sync<T: Sync>(_: T) {}
struct Automatic(AtomicUsize);
impl Automatic {
fn new() -> Automatic { Automatic(AtomicUsize::new(0)) }
}
fn main() {
ensure_sync(AtomicUsize::new(0));
ensure_sync(Automatic::new());
ensure_sync(Cell::new(0));
}
因为
Cell::new(0)
的所有字段都是Automatic
,所以仅在Sync
行上出错,Sync
为Foo
。关于
Rc
,Sync
既不是Send
也不是Foo
,因此编译器实际上也不会为您实现任何一种。Sync
可以是Foo
吗?我相信1。只要不对基于不变引用的模块添加任何其他操作即可。现在或将来。
Send
可以是Cell
吗?我同意您的结论,但是我认为您错过了另一种修改
drop
的方法:Sync
。因此,实际上,您似乎通过使用
Send
而非Send
的基础类型想出了Sync
而不是unsafe
的类型。这可能是我的 Nerd 感觉,我觉得这很有趣:)1在处理ojit_code代码时,我永远不会确定任何事情。愚弄自己以为是安全的,这很容易,仅仅是因为一个很小的细节就引起了人们的注意。
关于multithreading - 这个经过稍微修改的Rc <()>是Sync,但不是Send,对吧?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41846177/