Arc引用字段成员

时间:2017-01-20 18:29:02

标签: multithreading rust

我正在尝试生成一组给定的线程,并且每个线程执行一个长时间运行的操作。我将一个结构传递给每个工作线程作为给定线程的内部状态。所述结构的集合保存在向量中,是Master结构的一部分。

编译器拒绝我将结构的内部成员传递给Arc::new()

use std::thread;
use std::sync::Arc;

struct Worker {
   name: String,
}

struct Master {
    workers: Vec<Worker>,
}

impl Worker {
    fn start(&self) {
        println!("My name is {} and I'm working!", self.name);
        thread::sleep_ms(100_000);
    }
}

impl Master {
    pub fn run_test(&mut self) {
        for i in 0..10 {
            self.workers.push(Worker { 
                name: String::new() + "Worker" + &i.to_string() 
            });
        }
        let mut data = Arc::new(self.workers);

        for i in 0..10 {
            let local_data = data.clone();
            thread::spawn(move || {
                local_data[i].start();
            });
        }

        thread::sleep_ms(100_000);
    }
}


fn main() {
    let mut master = Master { workers: vec![] };
}

错误消息:

error[E0507]: cannot move out of borrowed content
  --> <anon>:26:33
   |
26 |         let mut data = Arc::new(self.workers);
   |                                 ^^^^ cannot move out of borrowed content

我做错了什么?这是惯用的Rust吗?

1 个答案:

答案 0 :(得分:6)

欢迎使用所有权。

在Rust中,任何单个数据都有一个且只有一个所有者。不要被RcArc愚弄:它们是单个(不可见)所有者之上的共享界面。

表达所有权的最简单方法是按值

struct Master {
    workers: Vec<Worker>
}

此处,Master拥有Vec<Worker>,其本身拥有多个Worker

类似地,按值(例如fn new(t: T) -> Arc<T>)获取参数的函数会获得其参数的所有权。

这就是问题所在:

Arc::new(self.workers)

意味着你同时:

  • 声称Masterworkers
  • 的所有者
  • 声称Arcworkers
  • 的所有者

鉴于一个且只有一个所有者的规则,这显然是难以处理的。

那么,你如何欺骗并拥有一个单一数据的多个共同所有者?

嗯...使用RcArc

struct Master {
    workers: Arc<Vec<Worker>>
}

现在创建data非常简单:

let data = self.workers.clone();

会创建一个新的Arc(只会碰到引用计数)。

但是,这并不是全部。借款系统的核心原则是:别名XOR可变性

由于Arc是关于别名的,因此会阻止可变性。您不能再将工作人员插入self.workers了!

有多种解决方案,例如在构建向量之前推迟self.workers的初始化,但最常见的是使用单元格或互斥锁,即Rc<RefCell<T>>Arc<Mutex<T>>(或Arc<RwLock<T>>)。

RefCellMutex是将借用检查从编译时移动到运行时的包装器。这提供了更多的灵活性,但可能导致运行时混乱而不是编译时错误,因此最好用作最后的手段。