⛳
[Rust]Mutexを使った並行処理
はじめに
Rustでは並行プログラミングを安全かつ簡単に行えるように、いくつかの同期プリミティブが提供されています。その一つがMutex
です。Mutex
は排他制御を提供し、一度に1つのスレッドだけがデータにアクセスできるようにします。
実装
RustのMutex
はstd::sync::Mutex
モジュールに定義されています。Mutex
を使用するには、データをMutex
でラップし、そのMutex
を共有リソースとして使用します。
またArcは参照のカウントを行うスマートポインタです。Atomically Reference Countedの略です。
以下の例では、複数のスレッドから共有カウンタに安全にアクセスする方法を示します。
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
fn main() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
// counterが何回使われてるかを表示します
println!("count first: {}", Arc::strong_count(&counter));
for i in 0..3 {
let counter_arc = Arc::clone(&counter);
println!("count: {}", Arc::strong_count(&counter));
let handle = thread::spawn(move || {
println!("Thread {} attempting to acquire lock...", i);
let mut num = counter_arc.lock().unwrap();
println!("Thread {} acquired lock", i);
*num += 1;
println!("Thread {} incremented counter to {}", i, *num);
// スレッドがロックを持っている間に少しスリープして、
// 他のスレッドがロックを待っていることを示します
thread::sleep(Duration::from_millis(50));
println!("Thread {} releasing lock", i);
});
handles.push(handle);
}
for handle in handles {
// join()でスレッドが終わるのを待つ
handle.join().unwrap();
}
println!("Result: {}", *counter.lock().unwrap());
}
joinに関しては日本語ドキュメントが詳しいです。
こちらの実行結果は以下です。
count first: 1
count: 2
count: 3
count: 4
Thread 0 attempting to acquire lock...
Thread 1 attempting to acquire lock...
Thread 1 acquired lock
Thread 1 incremented counter to 1
Thread 2 attempting to acquire lock...
Thread 1 releasing lock
Thread 0 acquired lock
Thread 0 incremented counter to 2
Thread 0 releasing lock
Thread 2 acquired lock
Thread 2 incremented counter to 3
Thread 2 releasing lock
Result: 3
全てのスレッド内の処理が始まる前にprintln!("count: {}", Arc::strong_count(&counter));
が実行されてるので、スレッドを建てる処理はそこそこ重いのかなぁと思います。
あとは、各スレッドに関して、acquired lock
からreleasing lock
の間に別のスレッドのacquired lock
がないことが分かります。
つまり、Mutexを使うことで、一つの変数に対して同時に処理が行われないということが分かりました。
Discussion