【问题标题】:This slightly modified Rc<()> is Sync, but not Send, right?这个稍加修改的 Rc<()> 是 Sync,但不是 Send,对吧?
【发布时间】:2017-01-25 07:55:05
【问题描述】:

我搜索了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())
    }
}

我知道编译器不会为我的类型自动实现SendSync;但我对我可以安全地手动实现的东西感兴趣。我认为:

  • 它应该能够实现Sync:拥有对Foo不可变引用不会让我们对它做任何事情(因为我们只能通过可变/独占引用调用my_clone())。而且不做任何事情,什么都不会出错,对吧?

  • 应该能够实现Send:我们可以在主线程中(在启动另一个线程之前)克隆我们的Foo以获得第二个对象。现在两个对象共享一些内存(引用计数,存储在Cell&lt;usize&gt; 中)。如果我现在可以将其中一个对象发送到另一个线程,那么两个线程都将拥有Foo 的所有权,引用相同的内存。因此,两个对象可以同时调用my_clone(),从而导致对引用计数的同时、非同步、可变访问(数据竞争)。

这个推理是正确的还是我遗漏了什么?

【问题讨论】:

    标签: multithreading rust


    【解决方案1】:

    我知道编译器不会为我的类型自动实现 SendSync

    确实,编译器会自动为您实现SendSync,前提是它可以确定这样做是安全的。

    这个小程序:

    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) 行上的错误,AutomaticSync,因为它的所有字段都是Sync


    关于FooRc既不是Sync也不是Send,所以编译器确实不会为你实现。

    Foo可以是Sync吗?

    相信1是的。只要没有其他操作被添加到对不可变引用进行操作的模块中。现在或将来。

    Foo可以是Send吗?

    我同意你的结论,但我认为你错过了另一种修改 Cell 的方法:drop

    因此,确实,您似乎通过使用Send 而不是Sync 的基础类型来提出Sync 而不是Send 的类型。这可能是我的书呆子感觉,我觉得这很有趣:)

    1在处理unsafe 代码时,我什么都不确定。很容易自欺欺人地认为某事是安全的,仅仅因为一个微小的细节没有引起注意。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-09-17
      • 2013-11-04
      • 2012-04-18
      • 1970-01-01
      • 2011-10-28
      • 2016-12-30
      • 2021-08-29
      • 2011-11-26
      相关资源
      最近更新 更多