在线程之间可变地共享i32

use*_*021 2 multithreading rust

我是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));
    }
}
Run Code Online (Sandbox Code Playgroud)

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

ken*_*ytm 9

请阅读The Rust Book"并发"章节"安全共享可变状态"部分,它将详细说明如何执行此操作.

简而言之:

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

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

您可能还想阅读:

  • 对于一般类型`T`,确实需要`Mutex <T>`.某些类型的另一个选择是在[`std :: sync :: atomic`](https://doc.rust-lang.org/std/sync/atomic/index.html)中使用包装类型,例如[` AtomicIsize`](https://doc.rust-lang.org/std/sync/atomic/struct.AtomicIsize.html)提供了诸如`fetch_add`或`compare_and_swap`之类的方法.(您还必须指定要强制执行的内存排序.) (5认同)

tre*_*tcl 9

另一个答案解决了任何类型的问题,但正如 pnkfelix观察到的那样,原子包装类型是另一种适用于 的特定情况的解决方案i32

从 Rust 1.0 开始,您可以使用AtomicBoolAtomicPtr<T>AtomicIsize来同步对、和值的AtomicUsize多线程访问。在 Rust 1.34 中,几个新类型已经稳定,包括. (查看当前列表的文档。)bool*mut TisizeusizeAtomicAtomicI32std::sync::atomic

使用原子类型很可能比锁定 aMutex或更有效RwLock,但需要更多地关注内存排序的低级细节。如果您的线程共享的数据多于一种标准原子类型所能容纳的数据,您可能需要一个Mutex而不是多个Atomics。

也就是说,这是 kennytm 的答案的一个版本,使用AtomicI32而不是Mutex<i32>

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

Arc共享所有权仍然需要(但请参阅如何将对堆栈变量的引用传递给线程?)。

选择正确的内存Ordering绝非易事。SeqCst是最保守的选择,但如果只有一个内存地址被共享,Relaxed也应该可行。请参阅下面的链接了解更多信息。

链接

  1. std::sync::atomic模块文档
  2. 原子Rustonomicon的章节)
  3. 用于并发操作和原子指令的 LLVM 内存模型和并发指南