12

编者注:此代码示例来自 Rust 1.0 之前的版本,在语法上不是有效的 Rust 1.0 代码。

是否可以在 Rust 中的多个线程之间共享一个可变变量?鉴于以下情况:

fn main() {

    let mut msg = "Hi";
    // ...
    msg = "Hello, World!";

    do spawn {
        println(msg);
    }

    do spawn {
        println(msg);
    }

}

我收到此错误:

错误

该变量只需要对生成的线程是只读的。该变量必须是可变的,因为我真正想做的是在多个线程之间共享一个 HashMap。据我所知,除非它是可变的,否则无法填充 HashMap。即使有办法做到这一点,我仍然有兴趣知道如何完成这样的事情。

谢谢!

4

4 回答 4

9

该限制将在该语言的未来版本中删除。也就是说,您可以let msg = msg;在第一个do spawn. 这会将值移动msg到不可变的位置,从而有效地改变其可变性。

于 2013-07-26T18:21:57.913 回答
4

“一般”的答案(关于在共享时保持哈希表可变)是它不是直接可能的;Rust 专门设计用于禁止共享可变状态,以避免某些类型的错误并保护某些类型的安全性(以及出于其他原因)。

但是,可以创建类似于共享可变哈希表的东西。想象一下,单个任务有一个可变哈希表。您可以编写代码,以便其他任务可以向它发送消息说,本质上是“更新哈希表,以便 5 映射到 18”或“告诉我 7 映射到什么”。

这种间接实现了什么?首先,通过将值复制到消息中,另一个任务不可能践踏你正在阅读的内存(一个很大的安全问题),其次,通过间接编写代码,程序员更清楚什么是原子操作,什么不是原子操作;程序员会知道不要期望关于哈希表内容的两个连续消息不一定与哈希表的相同版本有关,关于两次连续读取的假设是完全有效的(至少在 Rust 中,由于一些其他限制)来自非共享的、可变的哈希表。

于 2013-07-26T19:34:13.210 回答
3

在共享字符串的情况下"Hello, World!",您只需将变量移回不可变位置(例如,let mut msg = .....; let msg = msg;)。

不过,您可能还希望避免为每个接收它的线程制作一个单独的哈希图副本,在这种情况下,您还需要将它放在一个std::sync::Arc(原子引用计数的包装器)中。

于 2013-07-26T18:18:50.463 回答
2

如果你的变量可以是原子的,你可以使用Arc引用计数和原子类型。AnArc是一个引用计数器,它的作用很像具有以下特性的语言中的垃圾收集器:

use std::sync::atomic;
use std::sync::Arc;

fn main() {
    let arc_num = Arc::new(atomic::AtomicU32::new(1337));
    let arc_num_clone = Arc::clone(&arc_num);

    let _get_borrowed_clone = move || {
        return arc_num_clone.load(atomic::Ordering::Relaxed);
    };

    arc_num.store(4242, atomic::Ordering::Relaxed)
}

操场

很可能,您不能使用原子类型,在这种情况下,您可以使用Arcand Mutex

use std::sync::{Arc, Mutex};

fn main() {
    let arc_num = Arc::new(Mutex::new(1337));
    let arc_num_clone = Arc::clone(&arc_num);

    let _get_borrowed_clone = move || {
        let out = *arc_num_clone.lock().unwrap();
        return out;
    };

    *arc_num.lock().unwrap() = 4242;
}

阅读有关此代码如何工作的更多信息

如果您想进一步了解这两种方法之间的区别,请阅读“互斥锁”和“原子操作”之间有什么区别吗?

于 2020-02-26T23:12:52.937 回答