#28共有状態と同期トレイ


このシリーズはRust公式文書で学んだ痕跡です
いくつかの資料を別のトピックに転送するには、チャンネルを使用します.
しかし、この3つのスレッドで使用し、他の3つのスレッドを共有したい場合は、
チャネルではなく、異なる方法を使うべきです.
ただし、複数のThreedでデータを共有する場合は、
多重所有権が適用されるため、複雑さはそれに応じて向上します.
Mutex Mutual Exclusion
mutexは1つのスレッドだけでデータにアクセスできるやつです.
mutexの資料にアクセスするときはロックをかけ、使用が完了したらロックを解除します.
lockは資料を追跡するための資料構造である.
別のスレッドにロックされている場合は、この資料にアクセスできません.
lockには次のルールがあります.
  • 資料を使用する前に、ロックを取得しようとする必要があります.
    You must attempt to acquire the lock before using the data.
  • mutexで保護された資料の使用が完了したら、他のスレッドから取得するにはロックを解除する必要があります.
    When you’re done with the data that the mutex guards, you must unlock the data so other threads can acquire the lock.
  • 他の言語では、ロック解除された部分を除き
    他の反発オブジェクトのロックを解除するなど、エラーが発生する可能性があります.
    RUSTは、リソースチェックと所有権チェックによってこれらのエラーを回避することができる.
    mutexも他の資料型のように、関連関数newによって生成される.
    生成時にmutexで保護される資料をパラメータで渡す.
    lockを得るためには、Mutex<T>の方法lockを呼び出す必要がある.lockは、LockResultの値を返します.
    lockが得られると、スマートポインタMutexGuardの値に近づくことができる.MutexGuadrdはスマートポインタのようにDerefDropのトレイを実現した.
    こいつが範囲外で所有権を失うと、鍵は自動的に解除される.
    誤ったロック解除による問題を事前に阻止する.
    このように使用するのはあまり意味がありませんが、単一のスレッドでmutexを使用する例を作成しましょう.
    自動ロック解除のために、lockは括弧コードブロックで呼び出される.
    peter@hp-laptop:~/rust-practice/chapter16$ cargo new single_mutex
         Created binary (application) `single_mutex` package
    peter@hp-laptop:~/rust-practice/chapter16$ cd single_mutex/
    peter@hp-laptop:~/rust-practice/chapter16/single_mutex$ vi src/main.rs
    src/main.rs
    use std::sync::Mutex;
    
    fn main() {
        let m = Mutex::new(5);
    .
        {
            let mut num = m.lock().unwrap();
            *num = 6;
        }
    
        println!("m = {:?}", m);
    }
    peter@hp-laptop:~/rust-practice/chapter16/single_mutex$ cargo run
       Compiling single_mutex v0.1.0 (/home/peter/rust-practice/chapter16/single_mutex)
        Finished dev [unoptimized + debuginfo] target(s) in 0.25s
         Running `target/debug/single_mutex`
    m = Mutex { data: 6 }
    peter@hp-laptop:~/rust-practice/chapter16/single_mutex$ 
    個別のロック解除はありませんが、自動的にロック解除され、mにアクセスできることを確認できます.
    次に、複数のスレッドでmutexを使用する方法について正式に説明します.
    mutex変数も他の信頼タイプと同様に所有権によって管理されます.
    すなわち,あるThreedに直接入れると所有権が移され,他のThreedには渡されない.
    メッセージングは、この問題を解決するために送信者をコピーします.
    反発オブジェクトは、値が互いに同期しなければならないため、コピーによって解決できません.
    スマートポインタRc<T>を使いたいです.
    こいつはマルチスレッド環境では安全ではなく、使えない.
    幸いなことに、Rust標準ライブラリには次のものが含まれています.
    マルチスレッド環境では、Rc<T>のように利用可能なデータ型が存在する.Rc<T>原子を加えた原子をArc<T>と呼ぶ.
    原子性を確保するコードは、原子性を保証しないコードよりも性能がやや悪い.
    原子を必要としない単一スレッドではRc<T>それを必要とするマルチスレッドでは,性能の低下を考慮してArc<T>を用いた.Arc<T>を用いる方法はRc<T>とあまり変わらない.
    両者は同じAPIを提供しているが,内部の原子的保証は異なる.
    複数のスレッドでmutexを共有する例を見てみましょう.
    この例では、各スレッドのcounterの値を1増加します.
    peter@hp-laptop:~/rust-practice/chapter16/single_mutex$ cd ..
    peter@hp-laptop:~/rust-practice/chapter16$ cargo new mutex_counter
         Created binary (application) `mutex_counter` package
    peter@hp-laptop:~/rust-practice/chapter16$ cd mutex_counter/
    peter@hp-laptop:~/rust-practice/chapter16/mutex_counter$ vi src/main.rs
    src/main.rs
    use std::sync::{Mutex, Arc};
    use std::thread;
    
    fn main() {
        let counter = Arc::new(Mutex::new(0));
        let mut handles = vec![];
    
        for _ in 0..10 {
            let counter = Arc::clone(&counter);
            let handle = thread::spawn(move || {
                let mut num = counter.lock().unwrap();
                *num += 1;
            });
            handles.push(handle);
        }
    
        for handle in handles {
            handle.join().unwrap();
        }
    
        println!("Result: {}", *counter.lock().unwrap());
    }
    peter@hp-laptop:~/rust-practice/chapter16/mutex_counter$ cargo run
       Compiling mutex_counter v0.1.0 (/home/peter/rust-practice/chapter16/mutex_counter)
        Finished dev [unoptimized + debuginfo] target(s) in 0.43s
         Running `target/debug/mutex_counter`
    Result: 10
    peter@hp-laptop:~/rust-practice/chapter16/mutex_counter$ 
    10回の繰返しでは、各スレッドはcounter値1を良好に増加させたようだ.
    よく見ると、counterは変わらず、その値を変更していると発表しました.
    これは、Mutex<T>RefCell<T>のように内部可変性をサポートするため可能である.
    mutexを使用するときに注意しなければならないのは、次の点です.
    Rustは多様なセキュリティを保証できますが、Dedrockは阻止されません.
    死亡の可能性のある場所については、私たちは自分で処理しなければなりません.
    同期機能の実装
    rustの同期性は、言語自体ではなくMutexなどの標準ライブラリでサポートされています.
    必要に応じて、同期機能ライブラリを直接実装できます.
    crates.ioの上に他の人を置くことができます.
    同期機能を実現するために有用なトレイは、SendおよびSyncである.
    これはrust言語の角度から支持するやつです.
    こいつらはただ「こんな特性を持つ資料型」
    内部で処理するためだけに、
    単独で実施する必要のある方法は存在しない.
    このトレイはマークマークマークトレイと呼ばれています.SendトレイSendトレイを実装する資料型は、異なる所有権のサードパーティに移行することができる.
    Rustのほとんどの資料型はSendトレイを実現している.
    ただし、Rc<T>のような資料型も存在することに注意してください.Sendが実装されていないトレイのデータ型を別のトレイに転送する場合
    次のエラーが発生します.
    the trait Send is not implemented for 자료형
    実施形態Sendのトレイの特定のデータ型の組み合わせによって定義されるデータ型は、以下のものを含む.Sendトレイを自動的に実現します.SyncトレイSyncトレイを実装する資料型は、複数のスレッドで安全に参照することができる.Sendトレイと同様に、こいつもRUSTの大部分の資料型で実現された.
    また、実施形態Syncのパレットのデータ型の組み合わせにより定義されるデータ型
    自動実装Syncトレイも同様です.
    マルチスレッド環境では使えないと言っている奴らを処理しています
    威信が表れていないからだ.
    同期機能を備えた資料型は自分で作ることができますが、お勧めしません.
    安全性のために考えなければならないことが多く、効率的なことではありません.
    既存の同期機能を使用します.
    この記事の内容は、公式ドキュメントの第16章第3節Shared-State Concurrency&第16章第4節SyncとSend Traitの拡張可能通貨に相当します.