【crossbeamシリーズ】5 crossbeam-utilとcrossbeam-queue:実用的な小物


今回はcrossbeam-utilとcrossbeam-queueの中のいくつかのものと使い方を紹介します.

crossbeam-util


AtomicCell


これは同時版のstd::cell::Cellで、&selfに対して内部値を変更する効果を果たすことができて、主に以下の3つの方法を使います:
use crossbeam_utils::atomic::AtomicCell;

let a = AtomicCell::new(7);

assert_eq!(a.load(), 7);
assert_eq!(a.swap(8), 7);
assert_eq!(a.load(), 8);
a.store(9);
assert_eq!(a.load(), 9);


ShardedLock


これはstd::sync::RwLockと同じ機能ですが、相対的に読むのが速く、書くのが遅いです.実際に使用するときは、業務の特徴に応じて適宜選択することができます.
use crossbeam_utils::sync::ShardedLock;

let lock = ShardedLock::new(5);

//  
{
    let r1 = lock.read().unwrap();
    let r2 = lock.read().unwrap();
    assert_eq!(*r1, 5);
    assert_eq!(*r2, 5);
}

//  
{
    let mut w = lock.write().unwrap();
    *w += 1;
    assert_eq!(*w, 6);
}


WaitGroup


このGolangのsync.WaitGroupのように、いくつかのスレッドの作業がすべて完了するのを待つことができます.
use crossbeam_utils::sync::WaitGroup;
use std::thread;

//  
let wg = WaitGroup::new();

for _ in 0..4 {
    //  
    let wg = wg.clone();

    thread::spawn(move || {
        //  

        //  
        drop(wg);
    });
}

//  
wg.wait();

std::sync::Barrierとは異なり、WaitGroupのスレッド数は動的であることに注意してください.

scope


これはstd::threadと似ていますが、異なる点は、現在のスタックへの参照をサポートできることです.例えば、次のようなコードを書きたい場合は
let array = [1, 2, 3];
let mut guards = vec![];

for i in &array {
    let guard = std::thread::spawn(move || {
        println!("element: {}", i);
    });

    guards.push(guard);
}

for guard in guards {
    guard.join().unwrap();
}


コンパイラは&array'staticのライフサイクルを要求するため、エラーが発生します.しかし、実際にはjoinの部分なので、実は問題はありませんが、コンパイラは知りません.そこでscopeの活躍の場ができた.scopeがあればこう書くことができます.
let array = [1, 2, 3];

crossbeam::scope(|scope| {
    for i in &array {
        scope.spawn(move || {
            println!("element: {}", i);
        });
    }
});


時々threadよりも便利になるのではないでしょうか~

crossbeam-queue


これは同時版のキューであり,実際にはsendsyncのキューが実現されたと理解できる.容量が限られたArrayQueue
use crossbeam_queue::{ArrayQueue, PushError};

let q = ArrayQueue::new(2); //  2

assert_eq!(q.push('a'), Ok(()));
assert_eq!(q.push('b'), Ok(()));
assert_eq!(q.push('c'), Err(PushError('c')));
assert_eq!(q.pop(), Ok('a'));


容量が無限のSegQueue
use crossbeam_queue::{PopError, SegQueue};

let q = SegQueue::new();

q.push('a');
q.push('b');

assert_eq!(q.pop(), Ok('a'));
assert_eq!(q.pop(), Ok('b'));
assert_eq!(q.pop(), Err(PopError));


もちろん、容量の無限の代価は、容量を動的に増加させることであり、SegQueueの性能を低下させることである.crossbeam-queueはこれだけですが、簡単ではありませんか~

小結


実はutilの中にはまだいくつかのものがあります.ここでは話していません.みんな自分で見てもいいです.今期は些細なことを話しています.私たちのcrossbeamシリーズは今期で終わりましたか?そうですか.次回もあるかもしれませんが、お楽しみに~