Fu
Simple is Beautiful!

rust并发编程:线程高级用法

Barrier

Barrier 可以让多个线程同时都执行到在某一点后才能一起再往后执行:

use std::thread;
use std::sync::{Arc, Barrier};

fn main() {
    let mut handles = Vec::with_capacity(10);
    let barrier = Arc::new(Barrier::new(10));

    for _ in 0..10 {
        let b = barrier.clone();
        handles.push(thread::spawn(move|| {
            println!("before wait");
            b.wait();
            println!("after wait");
        }));
    }

    for handle in handles {
        handle.join().unwrap();
    }
}

上面程序会在所有线程都打印出 before wait 后,才会在继续运行,打印 after wait, 这样就保证了所有线程的 after wait 输出在 before wait 输出之后。

Condition Variables

Condition Variables 通常和 Mutex 一起使用,可以让线程挂起,直到某个条件发生后才会让线程继续执行下去:

use std::thread;
use std::sync::{Arc, Mutex, Condvar};

fn main() {
    let pair = Arc::new((Mutex::new(false), Condvar::new()));
    let pair2 = pair.clone();

    thread::spawn(move|| {
        let &(ref lock, ref cvar) = &*pair2;
        let mut started = lock.lock().unwrap();
        println!("changing started");
        *started = true;
        cvar.notify_one();
    });

    let &(ref lock, ref cvar) = &*pair;
    let mut started = lock.lock().unwrap();
    while !*started {
        started = cvar.wait(started).unwrap();
    }

    println!("started changed");
}

上面程序中,如果 started 变量是 false,主线程进入 while 循环体, 并在循环体内释放 started Mutex 锁后挂起线程,直到在其他线程中调用 cvar.notify_one 方法来唤醒主线程, 然后再次利用 while 循环条件判断,直到 started 的值为 true 才会继续执行循环之后的代码。

Call Once

有时,多线程中,有些函数只允许被调用一次,通常这类函数是用来在线程间初始化一些全局变量的, 这样,无论哪个线程先调用这个函数,都会保证全局变量只会被初始化一次, 随后的其他线程的调用就忽略会这个函数体了。

use std::thread;
use std::sync::{Once, ONCE_INIT};

static mut VAL: usize = 0;
static INIT: Once = ONCE_INIT;

fn main() {
    let handle1 = thread::spawn(move || {
        INIT.call_once(|| {
            unsafe {
                VAL = 1;
            }
        });
    });

    let handle2 = thread::spawn(move || {
        INIT.call_once(|| {
            unsafe {
                VAL = 2;
            }
        });
    });

    handle1.join().unwrap();
    handle2.join().unwrap();

    println!("{}", unsafe { VAL });
}

上面程序运行结果取决于哪个线程第一次调用 INIT.call_once, 如果是 handle1,那么结果是 1, 如果是 handle2,那么结果是 2

线程局部变量

rust 中用 thread_local 宏来初始化线程局部变量,并在线程内部用该变量的 with 方法来获取该变量值:

use std::cell::RefCell;
use std::thread;

thread_local! {
    static FOO: RefCell<u32> = RefCell::new(1);
}

fn main() {
    FOO.with(|foo| {
        assert_eq!(*foo.borrow(), 1);
        *foo.borrow_mut() = 2;
    });

    // each thread starts out with the initial value of 1
    thread::spawn(move|| {
        FOO.with(|foo| {
            assert_eq!(*foo.borrow(), 1);
            *foo.borrow_mut() = 3;
        });
    });

    // we retain our original value of 2 despite the child thread
    FOO.with(|foo| {
        assert_eq!(*foo.borrow(), 2);
    });
}
rust14concurrency4
2018-05-14 15:40:59