我对Rust还是很陌生,我试图将我制作的Go Web搜寻器移植到Rust。在Go中,我创建了一个哈希图,供多个工作人员使用(并共享)(执行产生相同功能的例程)。使用Mutexes可以轻松解决该问题,但我无法掌握如何在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
中,我添加了运行功能:
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的方法,但是有人可以建议实现多线程爬虫的最佳方法是什么吗?
非常感谢
问题不在于HashSet,而在于队列。如果您用标准库中的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),
},
_ => {}
}
}
}
最大的变化是我从match语句之外的队列中弹出。我认为如果比赛中有整个.lock().unwrap().pop()
语句,则将为比赛块的整个内容持有该锁。
但是,如果您对所使用的队列箱进行同样的操作,我不确定为什么它不起作用。我也是Rust的初学者,所以其中一些我仍然不清楚。
我对您的代码所做的更改可以在这里看到:https://pastebin.com/ZrXrsgzf。我对此进行了测试,并且它可以运行(至少它超过了最初卡住的位置)。
我最近还在Rust中实现了一个Web搜寻器,并撰写了有关它的文章here。