如何在不使用跟踪句柄的情况下等待未知数量的Rust线程完成?

时间:2017-07-05 03:07:49

标签: multithreading rust barrier

有哪些好方法可以调整此Barrier示例来处理两个差异:

  1. 事先不知道项目数(例如,在将大文件拆分成行的情况下)

  2. 没有跟踪线程句柄(例如,在下面的示例中没有使用handles向量)。动机是这样做会增加额外的开销。

  3. 示例代码:

    use std::sync::{Arc, Barrier};
    use std::thread;
    
    let mut handles = Vec::with_capacity(10);
    let barrier = Arc::new(Barrier::new(10));
    for _ in 0..10 {
        let c = barrier.clone();
        handles.push(thread::spawn(move|| {
            // do some work
            c.wait();
        }));
    }
    // Wait for other threads to finish.
    for handle in handles {
        handle.join().unwrap();
    }
    

    代码段略微改编自Barrier docs

    我想到的第一件事就是(如果可能的话)改变Barrier的内在价值;但是,API不提供对num_threads结构的Barrier属性的可变访问。

    另一个想法是不使用Barrier而是使用AtomicUsize编写自定义逻辑。

    我愿意在Rust中学习最符合人体工程学/习惯用法的方法。

1 个答案:

答案 0 :(得分:1)

您可以在atomic上使用spinlock来等待所有线程退出。当然,您可以将Arc<AtomicUsize>传递给每个线程,而不是使用静态原子。

Ordering::SeqCst可能过于强大,但并发编程很难,而且我不确定如何放宽这种排序。

虽然可以这样做,但创建线程的成本可能会使这种微优化相形见绌。此外,值得考虑的是,忙碌的等待会降低程序的性能。

use std::panic;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
use std::thread;
use std::time::Duration;

static GLOBAL_THREAD_COUNT: AtomicUsize = ATOMIC_USIZE_INIT;

fn main() {
    for i in 0..10 {
        // mark that the thread is about to run
        // we need to do it in the main thread to prevent spurious exits
        GLOBAL_THREAD_COUNT.fetch_add(1, Ordering::SeqCst);
        thread::spawn(move|| {
            // We need to catch panics to reliably signal exit of a thread
            let result = panic::catch_unwind(move || {
                // do some work
                println!("{}-th thread reporting", i+1);
            });
            // process errors
            match result {
                _ => {}
            }
            // signal thread exit
            GLOBAL_THREAD_COUNT.fetch_sub(1, Ordering::SeqCst);
        });
    }
    // Wait for other threads to finish.
    while GLOBAL_THREAD_COUNT.load(Ordering::SeqCst) != 0 {
        thread::sleep(Duration::from_millis(1)); 
    }
}

Playground link