Programming Rustの 465 ページには、コードと説明があります (強調は私が追加しました)。
use std::sync::Arc; fn process_files_in_parallel(filenames: Vec<String>, glossary: Arc<GigabyteMap>) -> io::Result<()> { ... for worklist in worklists { // This call to .clone() only clones the Arc and bumps the // reference count. It does not clone the GigabyteMap. let glossary_for_child = glossary.clone(); thread_handles.push( spawn(move || process_files(worklist, &glossary_for_child)) ); } ... }
用語集のタイプを変更しました。分析を並行して実行するには、呼び出し元は を実行して、ヒープに移動された
Arc<GigabyteMap>
へのスマート ポインターである を渡す必要があります。用語集.clone() を呼び出すと、全体ではなく、スマート ポインターのコピーが作成されます。これは、参照カウントをインクリメントすることになります。この変更により、参照の有効期間に依存しなくなるため、プログラムはコンパイルおよび実行されます。スレッドが を所有している限り、たとえ親スレッドが早期にベイル アウトしたとしても、マップは存続します。のデータは不変であるため、データ競合は発生しません。GigabyteMap
Arc::new(giga_map)
Arc
GigabyteMap
Arc<GigabyteMap>
Arc
次のセクションでは、これをレーヨンで書き直したものを示します。
extern crate rayon; use rayon::prelude::*; fn process_files_in_parallel(filenames: Vec<String>, glossary: &GigabyteMap) -> io::Result<()> { filenames.par_iter() .map(|filename| process_file(filename, glossary)) .reduce_with(|r1, r2| { if r1.is_err() { r1 } else { r2 } }) .unwrap_or(Ok(())) }
&GigabyteMap
ではなくレーヨンを使用するように書き直されたセクションで確認できますArc<GigabyteMap>
。ただし、これがどのように機能するかについては説明していません。なぜレーヨンは必要ないのですArc<GigabyteMap>
か? Rayon はどのようにして直接参照を受け入れることを回避しますか?