在线程之间可变地共享i32

问题描述 投票:1回答:2

我是Rust和线程的新手,我正在尝试打印一个数字,同时在另一个线程中添加它。我怎么能做到这一点?

use std::thread;
use std::time::Duration;

fn main() {
    let mut num = 5;
    thread::spawn(move || {
        loop {
            num += 1;
            thread::sleep(Duration::from_secs(10));
        }
    });
    output(num);
}

fn output(num: i32) {
    loop {
        println!("{:?}", num);
        thread::sleep(Duration::from_secs(5));
    }
}

上面的代码不起作用:它总是只打印5,好像数字永远不会增加。

multithreading rust
2个回答
8
投票

请阅读"Concurrency" chapter, "Safe Shared Mutable State" section of The Rust Book,它详细解释了如何做到这一点。

简而言之:

  1. 你的程序不起作用,因为num被复制,所以output()和线程在数字的不同副本上运行。如果num不可复制,Rust编译器将无法编译并出现错误。
  2. 由于您需要在多个线程之间共享相同的变量,因此需要将其包装在Arc(原子引用计数变量)中
  3. 由于你需要修改Arc中的变量,你需要把它放在MutexRwLock中。您使用.lock()方法从Mutex获取可变引用。该方法将确保在该可变引用的生命周期内对整个过程的独占访问。
use std::thread;
use std::time::Duration;
use std::sync::{Arc, Mutex};

fn main(){
    let num = Arc::new(Mutex::new(5));
    // allow `num` to be shared across threads (Arc) and modified
    // (Mutex) safely without a data race.

    let num_clone = num.clone();
    // create a cloned reference before moving `num` into the thread.

    thread::spawn(move || {
        loop {
            *num.lock().unwrap() += 1;
            // modify the number.
            thread::sleep(Duration::from_secs(10));
        }
    });

    output(num_clone);
}

fn output(num: Arc<Mutex<i32>>){
    loop {
        println!("{:?}", *num.lock().unwrap());
        // read the number.
        //  - lock(): obtains a mutable reference; may fail,
        //    thus return a Result
        //  - unwrap(): ignore the error and get the real
        //    reference / cause panic on error.
        thread::sleep(Duration::from_secs(5));
    }
}

您可能还想阅读:


2
投票

另一个答案解决了任何类型的问题,但作为pnkfelix Share mutable object between threads,原子包装类型是另一种解决方案,将适用于Arc<Mutex<i32>>的特定情况。

自Rust 1.0以来,您可以使用Arc<i32>When would you use a Mutex without an Arc?Arc<Mutex<i32>>Mutex<i32>来同步多线程访问observesi32AtomicBoolAtomicPtr<T>值。在Rust 1.34中,几种新的AtomicIsize类型已经稳定,包括AtomicUsize。 (查看bool文档以获取当前列表。)

使用原子类型最有可能比锁定*mut Tisize更有效,但需要更多关注内存排序的低级细节。如果你的线程共享的数据超过了一个标准原子类型的数据,你可能需要一个usize而不是多个Atomics。

也就是说,这是使用AtomicI32而不是std::sync::atomic的kennytm答案的一个版本:

Mutex

共享所有权仍需要RwLock(但请参阅Mutex)。

选择正确的记忆Atomic远非微不足道。 AtomicI32是最保守的选择,但如果只有一个内存地址被共享,Mutex<i32>也应该工作。有关更多信息,请参阅以下链接。

链接

  1. use std::sync::{ atomic::{AtomicI32, Ordering}, Arc, }; use std::thread; use std::time::Duration; fn main() { let num = Arc::new(AtomicI32::new(5)); let num_clone = num.clone(); thread::spawn(move || loop { num.fetch_add(1, Ordering::SeqCst); thread::sleep(Duration::from_secs(10)); }); output(num_clone); } fn output(num: Arc<AtomicI32>) { loop { println!("{:?}", num.load(Ordering::SeqCst)); thread::sleep(Duration::from_secs(5)); } }
  2. Arc(The Rustonomicon的章节)
  3. How can I pass a reference to a stack variable to a thread?Ordering
© www.soinside.com 2019 - 2024. All rights reserved.