Caution

お使いのブラウザはJavaScriptが実行できない状態になっております。
当サイトはWebプログラミングの情報サイトの為、
JavaScriptが実行できない環境では正しいコンテンツが提供出来ません。
JavaScriptが実行可能な状態でご閲覧頂くようお願い申し上げます。

Rust辞典

  1. トップページ
  2. Rust辞典
  3. Mutex<T> / RwLock<T>

Mutex<T> / RwLock<T>

複数スレッドで同じデータを安全に共有・変更するには、Rustの『Mutex<T>』や『RwLock<T>』を使います。どちらも『Arc<T>』と組み合わせて複数スレッドに共有します。

構文
use std::sync::{Arc, Mutex, RwLock};

// Mutex — 排他ロック(1スレッドのみ書き込み可能)
let mutex = Arc::new(Mutex::new(0i32));
let m = Arc::clone(&mutex);
let mut val = m.lock().unwrap();  // ロックを取得します(他スレッドはブロック)。
*val += 1;                         // ロック中に変更します。
// val がスコープを抜けると自動でロック解放されます。

// RwLock — 読み取りロック(複数読み取り可 / 書き込みは排他)
let rwlock = Arc::new(RwLock::new(vec![1, 2, 3]));
let r = rwlock.read().unwrap();    // 複数スレッドが同時に読み取れます。
let mut w = rwlock.write().unwrap(); // 書き込み時は他スレッドをブロックします。
メソッド一覧
メソッド概要
Mutex::new(val)値を Mutex でラップして生成します。
mutex.lock()ロックを取得します。Result<MutexGuard> を返します。他スレッドがロック中はブロックします。
mutex.try_lock()ロックを即座に試みます。取得できなければ Err を返します(ブロックしません)。
MutexGuard(guard)スコープを抜けると自動でロックを解放します(RAII)。
RwLock::new(val)値を RwLock でラップして生成します。
rwlock.read()読み取りロックを取得します。複数スレッドが同時に取得可能です。
rwlock.write()書き込みロックを取得します。全ての読み取り・書き込みロックが解放されるまでブロックします。
Arc::new(val)スレッド安全な参照カウントポインタです。Mutex/RwLock と組み合わせて使います。
Arc::clone(&arc)参照カウントを増やしてクローンします。スレッドへの共有に使います。
サンプルコード
use std::sync::{Arc, Mutex, RwLock};
use std::thread;

fn main() {
    // --- Mutex の基本 ---
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    // 10スレッドが並行してカウンターをインクリメントします。
    for _ in 0..10 {
        let c = Arc::clone(&counter);
        let h = thread::spawn(move || {
            let mut val = c.lock().unwrap();  // ロックを取得します。
            *val += 1;
            // val がスコープを抜けるとロックが解放されます。
        });
        handles.push(h);
    }

    for h in handles {
        h.join().unwrap();
    }
    println!("カウンター: {}", *counter.lock().unwrap());  // 10

    // --- デッドロックを避けるためロックを早めに解放します ---
    let data = Arc::new(Mutex::new(vec![1, 2, 3]));
    {
        let mut guard = data.lock().unwrap();
        guard.push(4);
    }  // guard がここでドロップされロックが解放されます。
    println!("data: {:?}", *data.lock().unwrap());  // [1, 2, 3, 4]

    // --- RwLock — 読み取りが多い場合に有効 ---
    let config = Arc::new(RwLock::new(String::from("initial")));
    let mut handles = vec![];

    // 複数の読み取りスレッド
    for i in 0..3 {
        let c = Arc::clone(&config);
        let h = thread::spawn(move || {
            let val = c.read().unwrap();  // 複数スレッドが同時に読めます。
            println!("読み取りスレッド{}: {}", i, *val);
        });
        handles.push(h);
    }

    // 書き込みスレッド(全読み取りロック解放後に実行されます)
    {
        let c = Arc::clone(&config);
        let h = thread::spawn(move || {
            let mut val = c.write().unwrap();
            *val = String::from("updated");
            println!("書き込み完了");
        });
        handles.push(h);
    }

    for h in handles {
        h.join().unwrap();
    }
    println!("最終値: {}", *config.read().unwrap());  // updated
}
概要

『Mutex』は1スレッドのみが同時にデータにアクセスできる排他ロックです。『lock()』でロックを取得し、返ってきた『MutexGuard』がスコープを抜けると自動でロックが解放されます(RAII)。

『RwLock』は複数スレッドが同時に読み取れますが、書き込みは排他になります。読み取りが多く書き込みが少ない場合は『Mutex』より効率的です。

デッドロック注意: 同じスレッドが同じ『Mutex』のロックを2回取得しようとするとデッドロックします。ロックはできるだけ短いスコープで使い、ロック中に別のロックを取得しないようにしてください。スレッドの基本はstd::thread::spawn() / join()を、チャンネルはstd::sync::mpsc チャンネルを参照してください。

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。