Rust - лучший способ поделиться хэш-сетом в структуре между несколькими работниками - PullRequest
0 голосов
/ 07 февраля 2020

Я довольно новичок в Rust, и я пытался портировать Go веб-сканер, который я сделал для Rust. В Go я создал хэш-карту, которая использовалась (и использовалась совместно) несколькими работниками (go подпрограммы, порождающие одну и ту же функцию). Это было легко решить с помощью мьютексов, но я не могу понять, как сделать то же самое в Rust.

Структура Crawler:

struct Crawler {
    client: reqwest::Client,
    target: String,
    visited: Arc<Mutex<HashSet<String>>>,
    queue: Arc<Mutex<Queue<String>>>,
    base_url: String,
    fetch_any_domain: bool,
    workers: u8,
}

В impl из Crawler Я добавил функцию запуска:

   fn run(&self) {
        {
            match self
                .queue
                .lock()
                .unwrap()
                .add(self.convert_link_to_abs(self.target.as_str()))
            {
                Err(e) => println!("{}", e),
                _ => (),
            }
        }

        while self.queue.lock().unwrap().size() > 0 {
            match self.queue.lock().unwrap().remove() {
                Ok(link) => match self.fetch(link.as_str()) {
                    Ok(content) => match self.get_links(content) {
                        Ok(()) => println!("added new links"),
                        Err(e) => println!("{}", e),
                    },
                    Err(e) => println!("{}", e),
                },
                Err(e) => println!("{}", e),
            }
        }
    }

И я пытался вызвать его одновременно с чем-то вроде этого:

        let mut threads = vec![];
        let c = Arc::new(Mutex::new(crawler));
        for _i in 0..workers {
            let cc = c.clone();
            threads.push(thread::spawn(move || {
                let guard = cc.lock().unwrap();
                guard.run();
            }));
        }

        for t in threads {
            let _ = t.join();
        }

Код каким-то образом запускается, но он застревает почти мгновенно без обработки ничего. Я уверен, что мне просто нужно привыкнуть к подходу Rust, но кто-нибудь может посоветовать, как лучше всего создать многопоточный сканер?

Большое спасибо

1 Ответ

1 голос
/ 03 марта 2020

Проблема не в HashSet, а в очереди. Если вы замените имеющуюся у вас очередь из этого внешнего ящика на Ve c из стандартной библиотеки и разделите некоторые операторы, она будет работать нормально.

fn run(&self) {
        {
            self.queue
                .lock()
                .unwrap()
                .push(self.convert_link_to_abs(self.target.as_str()))
        }

        while self.queue.lock().unwrap().len() > 0 {
            let x = self.queue.lock().unwrap().pop();
            match x {
                Some(link) => match self.fetch(&link) {
                    Ok(content) => match self.get_links(content) {
                        Ok(()) => println!("added new links"),
                        Err(e) => println!("{}", e),
                    },
                    Err(e) => println!("{}", e),
                },
                _ => {}
            }
        }
    }

Самое большое изменение в том, что я выскакиваю из очереди за пределами оператора сравнения. Я думаю, что если у вас есть весь оператор .lock().unwrap().pop() в совпадении, блокировка будет сохранена для всего содержимого блока совпадения.

Однако я не уверен, почему это не сработает, если вы сделаете то же самое с корзиной очередей, которую использовали. Я также новичок в Rust, поэтому кое-что из этого мне пока неясно.

Изменения, которые я внес в ваш код, можно увидеть здесь: https://pastebin.com/ZrXrsgzf. Я проверил это, и он работает (по крайней мере, он проходит, где он застрял изначально).

Я также недавно внедрил в Rust веб-сканер и написал об этом здесь .

...