Почему Rayon не требует Arc <_>? - PullRequest
1 голос
/ 23 декабря 2019

На странице 465 из Programming Rust вы можете найти код и пояснение (выделено мной)

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>, умный указатель на GigabyteMap, который был перемещен в кучу, выполнив Arc::new(giga_map). Когда мы вызываем glossary.clone (), мы копируем умный указатель 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(()))
}

В разделе, переписанном для использования Rayon, вы можете увидеть, что он принимает &GigabyteMap вместо Arc<GigabyteMap>. Они не объясняют, как это работает. Почему Район не требует Arc<GigabyteMap>? Как Району удается принять прямую ссылку?

1 Ответ

2 голосов
/ 23 декабря 2019

Район может гарантировать, что итератор не переживет текущий кадр стека, в отличие от того, что я предполагаю thread::spawn в первом примере кода. В частности, par_iter под колпаком использует что-то вроде функции scope Района, которая позволяет порождать единицу работы, которая «прикреплена» к стеку и присоединится до того, как стек закончится.

Поскольку Rayon может гарантировать (через границы времени жизни, с точки зрения пользователя), что задачи / потоки объединяются до выхода из функции, вызывающей par_iter, он может предоставить этот API, который более эргономичен для использования, чем стандартная библиотека thread::spawn.

Район подробно описывает это в документации к функции scope .

...