1

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() を呼び出すと、全体ではなく、スマート ポインターのコピーが作成されます。これは、参照カウントをインクリメントすることになります。この変更により、参照の有効期間に依存しなくなるため、プログラムはコンパイルおよび実行されます。スレッドが を所有している限り、たとえ親スレッドが早期にベイル アウトしたとしても、マップは存続します。のデータは不変であるため、データ競合は発生しません。GigabyteMapArc::new(giga_map)ArcGigabyteMapArc<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 はどのようにして直接参照を受け入れることを回避しますか?

4

1 に答える 1

2

Rayon はthread::spawn、最初のコード例とは異なり、イテレータが現在のスタック フレームより長く存続しないことを保証できます。具体的にpar_iterは、ボンネットの下では Rayon のscope関数のようなものを使用します。これにより、スタックに「接続」され、スタックが終了する前に参加する作業単位を生成できます。

Rayon は、関数呼び出しpar_iterが終了する前にタスク/スレッドが結合されることを (ユーザーの観点から、有効期限を介して) 保証できるため、標準ライブラリの よりも人間工学的に使用できるこの API を提供できますthread::spawn

Rayon は、scope関数のドキュメントでこれについて詳しく説明しています。

于 2019-12-23T02:55:38.430 に答える