简体   繁体   English

在 Rust 中的线程之间共享可变变量

[英]Sharing Mutable Variables Between Threads In Rust

Editor's note: This code example is from a version of Rust prior to 1.0 and is not syntactically valid Rust 1.0 code.编者注:此代码示例来自 Rust 1.0 之前的版本,在语法上不是有效的 Rust 1.0 代码。

Is it possible to share a mutable variable between multiple threads in Rust?是否可以在 Rust 中的多个线程之间共享可变变量? Given the following:鉴于以下情况:

fn main() {

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

    do spawn {
        println(msg);
    }

    do spawn {
        println(msg);
    }

}

I get this error:我收到此错误:

错误

The variable just needs to be readonly to the spawned threads.该变量只需要对生成的线程只读。 The variable has to be mutable though, because what I'm really trying to do is share a HashMap between multiple threads.但是变量必须是可变的,因为我真正想做的是在多个线程之间共享一个 HashMap。 As far as I know there is no way to populate a HashMap unless it's mutable.据我所知,除非它是可变的,否则无法填充 HashMap。 Even if there is a way to do that though, I'm still interested in knowing how to accomplish something like this in general.即使有办法做到这一点,我仍然有兴趣知道如何完成这样的事情。

Thank you!谢谢!

This restriction is slated to be removed in a future version of the language.此限制计划在该语言的未来版本中删除。 That said, you can fix this problem with let msg = msg;也就是说,您可以使用let msg = msg;解决这个问题let msg = msg; before the first do spawn .在第一次do spawn之前。 That will move the value msg into an immutable location, effectively changing its mutability.这会将值msg移动到一个不可变的位置,有效地改变它的可变性。

The "in general" answer (about keeping a hash table mutable while it's shared) is that it's not directly possible; “一般”的答案(关于在共享时保持哈希表可变)是它不是直接可能的; Rust was specifically designed to forbid the sharing of mutable state in order to avoid certain kinds of bugs and to protect certain kinds of safety (and for other reasons). Rust 专门设计用于禁止共享可变状态,以避免某些类型的错误并保护某些类型的安全(以及其他原因)。

However, it is possible to create something much like a shared mutable hash table.但是,可以创建类似于共享可变哈希表的东西。 Imagine that a single task has a mutable hash table.想象一下,单个任务有一个可变的哈希表。 You can write code so that other tasks can send it messages saying, essentially "Update the hash table so that 5 maps to 18" or "Tell me what 7 maps to".您可以编写代码,以便其他任务可以向其发送消息,说基本上是“更新哈希表,使 5 映射到 18”或“告诉我 7 映射到什么”。

What does this indirection accomplish?这个间接实现了什么? First, by copying values into messages, it's not possible for another task to trample all over memory that you're in the middle of reading (a big safety problem), and second, by writing the code indirectly, it's more clear to the programmer what is and is not an atomic operation;首先,通过将值复制到消息中,其他任务不可能践踏您正在阅读的所有内存(一个很大的安全问题),其次,通过间接编写代码,程序员更清楚什么是原子操作,什么不是; the programmer will know not to expect that two consecutive messages about hash table contents aren't necessarily about the same version of the hash table, an assumption that is perfectly valid (at least in Rust, thanks to some other restrictions) about two consecutive reads from an unshared, mutable, hash table.程序员会知道不要期望关于哈希表内容的两个连续消息不一定是关于哈希表的相同版本,关于两次连续读取的假设是完全有效的(至少在 Rust 中,由于一些其他限制)来自一个非共享的、可变的、哈希表。

In the case of sharing the string "Hello, World!"在共享字符串"Hello, World!" , you'd just need to move the variable back into an immutable location (eg, let mut msg = .....; let msg = msg; ). ,您只需要将变量移回一个不可变的位置(例如, let mut msg = .....; let msg = msg; )。

Probably you also want to avoid making a separate copy of the hashmap for each thread that's receiving it, though, in which case you'll want to also put it inside an std::sync::Arc (atomically reference counted wrapper).可能您还想避免为接收它的每个线程制作单独的哈希图副本,但是,在这种情况下,您还想将它放在std::sync::Arc (原子引用计数包装器)中。

If your variable can be atomic, you can use an Arc for reference counting and the atomic type.如果您的变量可以是原子的,您可以使用Arc进行引用计数和原子类型。 An Arc is a reference counter which acts a lot like the garbage collector in languages that have those: Arc是一个引用计数器,它的作用很像语言中的垃圾收集器,具有以下特性:

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)
}

playground 操场

Likely, you can't use an atomic type, in which case you can use Arc and Mutex :很可能,您不能使用原子类型,在这种情况下,您可以使用ArcMutex

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;
}

Read more about how this code works阅读有关此代码如何工作的更多信息

If you want to know more about the difference between these two approaches, read Is there any difference between "mutex" and "atomic operation"?如果您想更多地了解这两种方法之间的区别,请阅读“互斥锁”和“原子操作”之间有什么区别吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM