【问题标题】:Rust Multithreading only lock specific indices of vectorRust 多线程只锁定向量的特定索引
【发布时间】:2022-08-23 20:00:55
【问题描述】:

情况

  • 我有一个 f32 数组
  • 我有一些线程,每个线程都会改变数组的一小部分
  • 我不知道哪些索引会被改变
  • 每个线程都必须锁定数组,然后花费一些时间进行昂贵的计算
  • 之后,它会改变索引并释放数组
  • 看看下面注释的最小示例

问题

第一个线程将锁定数组,其他线程不能再编辑它。从而浪费了很多时间。需要编辑不同索引并且永远不会触及第一个线程所需的索引的其他线程可以同时执行。

可能的解决方案

  • 我知道数组的寿命比所有线程都长,所以不安全的 Rust 是一个可行的选择
  • 我已经为可能有相同问题的其他人发布了一个使用 2 个外部 crate 的解决方案。
  • 您可能会想出一个仅限 stdlib 的解决方案。

最小的例子:

use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;

use rand::Rng;

fn main() {
    // Store the mutex
    let container = Arc::new(Mutex::new([0.0; 10]));

    // This will keep track of the created threads
    let mut threads = vec![];

    // Create new Threads
    for _ in 0..10 {
        // Create a copy of the mutex reference
        let clone = Arc::clone(&container);

        threads.push(thread::spawn(move || {
            // The function somehow calculates the index that has to be changed
            // In our case its simulated by picking a random index to emphasize that we do not know the index
            let mut rng = rand::thread_rng();
            let index = rng.gen_range(0..10);

            // Unfortuantely we have to lock the array before the intense calculation !!!
            // If we could just lock the index of the array, other threads could change other indices in parallel
            // But now all of them need to wait for the lock
            let mut myarray = clone.lock().unwrap();

            // simulate intense calculation
            thread::sleep(Duration::from_millis(1000));

            // Now the index can be changed
            println!(\"Changing index {}\", index);
            myarray[index] += 1.0;
        }));
    }

    // Wait for all threads to finish
    for thread in threads {
        thread.join().unwrap();
    }

    // I know that myarray outlives the runtime of all threads.
    // Therefore someone may come up with an unsafe solution

    // Print the result
    println!(\"{:?}\", container);
}

    标签: multithreading rust


    【解决方案1】:

    这是我使用 atomic_float 和 crossbeam crate 提出的解决方案。

    它之所以有效,是因为横梁线程确保所有值都与范围一样长。
    AtomicF32 保护值免受并发访问

    use atomic_float::AtomicF32;
    use crossbeam::thread;
    use rand::Rng;
    use std::sync::atomic::Ordering;
    use std::thread::sleep;
    use std::time::Duration;
    
    fn main() {
        // Create a new atomic float array
        // atomic floats ensure that the value is protected from concurrent access
        // Thus locking only the indices
        // Notice the array is not mutable
        let myarray = [
            AtomicF32::new(0.0),
            AtomicF32::new(0.0),
            AtomicF32::new(0.0),
            AtomicF32::new(0.0),
            AtomicF32::new(0.0),
            AtomicF32::new(0.0),
            AtomicF32::new(0.0),
            AtomicF32::new(0.0),
            AtomicF32::new(0.0),
            AtomicF32::new(0.0),
        ];
    
        // This is a crossbeam thread
        thread::scope(|s| {
            // The loop has to be inside the scope
            // All threads spawned within cb_thread::scope must terminate before cb_thread::scope can return
            // That's how it makes sure the scoped variables exist at least as long as the spawned threads
            for _ in 0..10 {
                //Create a new thread
                s.spawn(|_| {
                    let mut rng = rand::thread_rng();
                    let index = rng.gen_range(0..10);
    
                    // Simulate heavy work
                    sleep(Duration::from_millis(3000));
    
                    // Now the index can be changed
                    println!("Changing index {}", index);
                    // This is the atomic operation. The value is therefore protected from concurrent access
                    myarray[index].fetch_add(1.0, Ordering::SeqCst)
                });
            }
        })
        .unwrap();
    
        println!("{:?}", myarray);
    }
    

    【讨论】:

      猜你喜欢
      • 2020-03-24
      • 2020-09-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多