Rust - 在多个工作人员之间共享结构中的哈希集的最佳方式

Rust - best way to share a hashset in a structure between multiple workers

我是 Rust 的新手,我正在尝试将我制作的 Go 网络爬虫移植到 Rust。 在 Go 中,我创建了一个 hashmap,它被多个 worker 使用(和共享)(生成相同函数的 go 例程)。使用 Mutexes 很容易解决这个问题,但我无法掌握如何在 Rust 中做同样的事情。

爬虫结构是:

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中我添加了运行函数:

   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();
        }

代码不知何故 运行s 但它几乎立即卡住了,没有处理任何东西。 我确定我只需要习惯 Rust 方法,但有人可以建议实现多线程爬虫的最佳方法是什么吗?

非常感谢

问题不在于哈希集,而在于队列。如果你用标准库中的 Vec 替换你从外部板条箱中获得的 Queue 并拆分一些语句,它会工作正常。

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() 语句,那么匹配块的全部内容都会被锁定。

但是,如果您对您使用的 Queue crate 执行相同操作,我不确定为什么它不起作用。我也是 Rust 初学者,所以其中一些我也不清楚。

可以在此处查看我对您的代码所做的更改:https://pastebin.com/ZrXrsgzf。我测试了它并且它运行了(至少它超过了它最初卡住的地方)。

我最近还用 Rust 实现了一个网络爬虫并写了一篇文章 here