这个稍加修改的 Rc<()> 是 Sync,但不是 Send,对吧?

Posted

技术标签:

【中文标题】这个稍加修改的 Rc<()> 是 Sync,但不是 Send,对吧?【英文标题】:This slightly modified Rc<()> is Sync, but not Send, right? 【发布时间】: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(),从而导致对引用计数的同时、非同步、可变访问(数据竞争)。

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

【问题讨论】:

【参考方案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 代码时,我什么都不确定。很容易自欺欺人地认为某事是安全的,仅仅因为一个微小的细节没有引起注意。

【讨论】:

以上是关于这个稍加修改的 Rc<()> 是 Sync,但不是 Send,对吧?的主要内容,如果未能解决你的问题,请参考以下文章

<select>隐藏边框及下拉箭头

jQuery---10. jQuery案例

MathJax的使用

想要修改init.rc文件怎么做呢?

P1757 通天之分组背包

学生管理系统(layUI Echarts)