【问题标题】:How to tell Rust to let me modify a shared variable hidden behind an RwLock?如何告诉 Rust 让我修改隐藏在 RwLock 后面的共享变量?
【发布时间】:2017-09-21 09:07:48
【问题描述】:

Safe Rust 要求所有参考资料如下:

  1. 一个或多个对资源的引用 (&T),
  2. 正是一个可变引用 (&mut T)。

我希望有一个Vec 可以被多个线程读取并由一个线程写入,但一次只能有一个线程(根据语言要求)。

所以我使用RwLock

我需要Vec<i8>。为了让它比 main 函数更长寿,我 Box 它然后我 RwLockthat 周围,像这样:

fn main() {
    println!("Hello, world!");
    let mut v = vec![0, 1, 2, 3, 4, 5, 6];
    let val = RwLock::new(Box::new(v));
    for i in 0..10 {
        thread::spawn(move || threadFunc(&val));
    }
    loop {
        let mut VecBox = (val.write().unwrap());
        let ref mut v1 = *(*VecBox);
        v1.push(1);
        //And be very busy.
        thread::sleep(Duration::from_millis(10000));
    }
}
fn threadFunc(val: &RwLock<Box<Vec<i8>>>) {
    loop {
        //Use Vec
        let VecBox = (val.read().unwrap());
        let ref v1 = *(*VecBox);
        println!("{}", v1.len());
        //And be very busy.
        thread::sleep(Duration::from_millis(1000));
    }
}

Rust 拒绝编译这个:

 capture of moved value: `val`
   --> src/main.rs:14:43
      |
   14 |         thread::spawn(move || threadFunc(&val));
      |                       -------             ^^^ value captured here after move
      |                       |
      |                       value moved (into closure) here

没有线程:

for i in 0..10 {
    threadFunc(&val);
}

它编译。问题在于关闭。我必须“移动”它,否则 Rust 抱怨它可以比 main 活得更久,我也不能克隆 valRwLock 没有实现 clone())。

我该怎么办?

【问题讨论】:

    标签: multithreading rust


    【解决方案1】:

    请注意,使用RwLockMutex 在结构上没有区别;他们只是有不同的访问模式。看 Concurrent access to vector from multiple threads using a mutex lock相关讨论。

    问题的中心在于您已将向量的所有权(在RwLock 中)转移到某个线程;因此您的主线程不再拥有它。您无法访问它,因为它已经消失了。

    实际上,您将遇到与尝试将向量传递给每个线程相同的问题。你只有一个向量可以赠送,所以只有一个线程可以拥有它。

    您需要线程安全的共享所有权,由Arc 提供:

    use std::sync::{Arc, RwLock};
    use std::thread;
    use std::time::Duration;
    
    fn main() {
        println!("Hello, world!");
        let v = vec![0, 1, 2, 3, 4, 5, 6];
        let val = Arc::new(RwLock::new(v));
    
        for _ in 0..10 {
            let v = val.clone();
            thread::spawn(move || thread_func(v));
        }
    
        for _ in 0..5 {
            {
                let mut val = val.write().unwrap();
                val.push(1);
            }
            thread::sleep(Duration::from_millis(1000));
        }
    }
    
    fn thread_func(val: Arc<RwLock<Vec<i8>>>) {
        loop {
            {
                let val = val.read().unwrap();
                println!("{}", val.len());
            }
            thread::sleep(Duration::from_millis(100));
        }
    }
    

    其他注意事项:

    1. 我删除了main 中的无限循环,这样代码才能真正完成。
    2. 我修复了所有编译器警告。如果您要使用编译语言,请注意警告。
      • 不必要的括号
      • snake_case 标识符。绝对不要PascalCase 用于局部变量;用于类型。 camelCase 不会在 Rust 中使用。
    3. 我添加了一些块来缩短读/写锁的寿命。否则会有很多争用,子线程永远没有机会获得读锁。
    4. let ref v1 = *(*foo); 是非惯用语。首选let v1 = &amp;**foo。感谢Deref,您甚至根本不需要这样做。

    【讨论】:

    • 谢谢,尽管使用drop() 变量而不是破解作用域不是更惯用吗?
    • @CharlesShiller 见What are the options to end a mutable borrow in Rust?。所以不,块是你能得到的惯用语。
    • @E_net4 在这种情况下,drop 应该可以工作,因为锁看起来不像是可变借用。我不知道这里的 either 比另一个更惯用; drop(vec) 对我来说只是看起来很奇怪。
    • 另外,也许它是我的 C,但 &amp;**foo*foo 不同吗?
    • @CharlesShiller 请参阅stackoverflow.com/a/28552082/155423 了解完整详情。 TL;DR,Rust 具有 Deref 特征,这意味着 &amp;* 并不总是无操作。
    猜你喜欢
    • 2019-11-27
    • 1970-01-01
    • 2014-09-23
    • 1970-01-01
    • 2018-01-08
    • 2022-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多