Send と Sync による拡張可能な並行性
興味深いことに、この章でここまで扱ってきた並行性機能のほとんどは、 言語そのものではなく標準ライブラリの一部でした。並行性を扱うための 選択肢は、言語や標準ライブラリに限られません。独自の並行性機能を 書くこともできますし、ほかの人が書いたものを使うこともできます。
しかし、標準ライブラリではなく言語に埋め込まれている並行性の重要な
概念の中には、std::marker トレイトである Send と Sync が
あります。
スレッド間で所有権を転送する
Send マーカートレイトは、Send を実装する型の値の所有権を
スレッド間で転送できることを示します。ほとんどすべての Rust の型は
Send を実装していますが、いくつか例外があり、そのひとつが Rc<T>
です。これは Send を実装できません。なぜなら、Rc<T> の値を
クローンして、そのクローンの所有権を別のスレッドに転送しようとすると、
両方のスレッドが同時に参照カウントを更新してしまう可能性があるからです。
このため、Rc<T> は、スレッドセーフ性のための性能上のペナルティを
払いたくないシングルスレッドの状況で使うために実装されています。
したがって、Rust の型システムとトレイト境界により、Rc<T> の値を
安全でない形で誤ってスレッド間で送ってしまうことは決してありません。
これをリスト16-14で試したとき、the trait `Send` is not implemented for `Rc<Mutex<i32>>` というエラーが出ました。Send を実装している
Arc<T> に切り替えると、コードはコンパイルできました。
Send 型だけで完全に構成される任意の型も、自動的に Send として
マークされます。第20章で説明する生ポインタを除き、ほとんどすべての
プリミティブ型は Send です。
複数のスレッドからアクセスする
Sync マーカートレイトは、Sync を実装する型が複数のスレッドから
参照されても安全であることを示します。言い換えると、&T
(T へのイミュータブル参照)が Send を実装していれば、任意の型 T
は Sync を実装します。これは、その参照を別のスレッドへ安全に送れる
ことを意味します。Send と同様に、すべてのプリミティブ型は Sync を
実装しており、Sync を実装する型だけで完全に構成される型も Sync を
実装します。
スマートポインタ Rc<T> も、Send を実装しないのと同じ理由で
Sync を実装しません。RefCell<T> 型(第15章で扱いました)と、
それに関連する Cell<T> 型の一群も Sync を実装しません。
RefCell<T> が実行時に行う借用チェックの実装はスレッドセーフでは
ありません。スマートポインタ Mutex<T> は Sync を実装しており、
「Mutex<T> への共有アクセス」 で見たように、
複数のスレッド間でアクセスを共有するために使えます。
Send と Sync を手動で実装するのは unsafe である
Send トレイトと Sync トレイトを実装するほかの型だけで完全に
構成される型も、自動的に Send と Sync を実装するため、これらの
トレイトを手動で実装する必要はありません。マーカートレイトであるため、
実装すべきメソッドすらありません。これらは、並行性に関する不変条件を
強制するのに役立つだけです。
これらのトレイトを手動で実装するには、unsafe な Rust コードを実装する
必要があります。unsafe な Rust コードの使い方については第20章で
説明します。今のところ重要なのは、Send と Sync の部品から
構成されていない新しい並行型を作るには、安全性の保証を保つために
慎重な検討が必要だということです。『The Rustonomicon』 には、
これらの保証と、それをどのように維持するかについての詳しい情報が
あります。
まとめ
この本で並行性を見るのはこれで最後ではありません。次の章では async プログラミングに焦点を当て、第21章のプロジェクトでは、この章の概念を、 ここで扱った小さな例よりも現実的な状況で使います。
前にも触れたように、Rust が並行性をどのように扱うかのうち、言語そのものの 一部であるものはごくわずかなので、多くの並行性ソリューションはクレートとして 実装されています。これらは標準ライブラリよりも速く進化するため、 マルチスレッドの状況で使う最新の最先端クレートについては、必ずオンラインで 調べるようにしてください。
Rust の標準ライブラリは、メッセージ受け渡しのためのチャネルと、
Mutex<T> や Arc<T> のような、並行コンテキストで安全に使える
スマートポインタ型を提供しています。型システムと借用チェッカーにより、
これらのソリューションを使うコードがデータ競合や無効な参照を抱えることは
ありません。いったんコードがコンパイルできれば、他の言語でよくある、
追跡しづらい種類のバグなしに、それが複数のスレッドで問題なく動作すると
安心してよいでしょう。並行プログラミングは、もはや恐れるべき概念では
ありません。さあ、恐れることなくプログラムを並行にしましょう!