How can I mutably share an i32 between threads?

前端 未结 2 1211
别跟我提以往
别跟我提以往 2020-12-07 04:37

I\'m new to Rust and threading and I\'m trying to print out a number while adding to it in another thread. How can I accomplish this?

use std::thread;
use st         


        
相关标签:
2条回答
  • 2020-12-07 05:04

    The other answer solves the problem for any type, but as pnkfelix observes, atomic wrapper types are another solution that will work for the specific case of i32.

    Since Rust 1.0, you can use AtomicBool, AtomicPtr<T>, AtomicIsize and AtomicUsize to synchronize multi-threaded access to bool, *mut T, isize and usize values. In Rust 1.34, several new Atomic types have been stabilized, including AtomicI32. (Check the std::sync::atomic documentation for the current list.)

    Using an atomic type is most likely more efficient than locking a Mutex or RwLock, but requires more attention to the low-level details of memory ordering. If your threads share more data than can fit in one of the standard atomic types, you probably want a Mutex instead of multiple Atomics.

    That said, here's a version of kennytm's answer using AtomicI32 instead of 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));
        }
    }
    

    Arc is still required for shared ownership (but see How can I pass a reference to a stack variable to a thread?).

    Choosing the right memory Ordering is far from trivial. SeqCst is the most conservative choice, but if there is only one memory address being shared, Relaxed should also work. See the links below for more information.

    Links

    1. std::sync::atomic module documentation
    2. Atomics (chapter of The Rustonomicon)
    3. LLVM Memory Model for Concurrent Operations and Atomic Instructions and Concurrency Guide
    0 讨论(0)
  • 2020-12-07 05:09

    Please read the "Shared-State Concurrency" chapter of The Rust Book, it explains how to do this in detail.

    In short:

    1. Your program does not work because num is copied, so output() and the thread operate on different copies of the number. The Rust compiler will fail to compile with an error if num is not copyable.
    2. Since you need to share the same variable between multiple threads, you need to wrap it in an Arc (atomic reference-counted variable)
    3. Since you need to modify the variable inside the Arc, you need to put it in a Mutex or RwLock. You use the .lock() method to obtain a mutable reference out of a Mutex. The method will ensure exclusive access across the whole process during the lifetime of that mutable reference.
    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));
        }
    }
    

    You may also want to read:

    • Why does Rust have mutexes and other sychronization primitives, if sharing of mutable state between tasks is not allowed?
    • What happens when an Arc is cloned?
    • How do I share a mutable object between threads using Arc? (for why we need Arc<Mutex<i32>> instead of Arc<i32>)
    • When would you use a Mutex without an Arc? (for why we need Arc<Mutex<i32>> instead of Mutex<i32>)
    0 讨论(0)
提交回复
热议问题