无法连接Vector语言 - Rust中存储的JoinHandles中的线程



我正在编写一个程序,该程序从网站列表中抓取数据并将其存储到一个名为Listing的结构体中,然后将其收集到一个名为Listings的最终结构体中。

use std::{ thread,
sync::{ Arc, Mutex }
};
fn main() {
// ... some declarations
let sites_count = site_list.len(); // site_list is a vector containing the list of websites
// The variable to be updated by the thread instances ( `Listing` is a struct holding the information ) 
let listings: Arc<Mutex<Vec<Vec<types::Listing<String>>>>> = Arc::new(Mutex::new(Vec::new()));
// A vector containing all the JoinHandles for the spawned threads
let mut fetch_handle: Vec<thread::JoinHandle<()>> = Vec::new();
// Spawn a thread for each concurrent website
for i in 0..sites_count { 
let slist = Arc::clone(&site_list);
let listng = Arc::clone(&listings);
fetch_handle.push(
thread::spawn(move || {
println!("⌛ Spawned Thread: {}",i);
let site_profile = read_profile(&slist[i]);
let results = function1(function(2)) // A long list of functions from a submodule that make the http request and parse the data into `Listing`
listng.lock().unwrap().push(results);
}));
}

for thread in fetch_handle.iter_mut() { 
thread.join().unwrap();
}
// This is the one line version of the above for loop - yields the same error.
// fetch_handle.iter().map(|thread| thread.join().unwrap()); 
// The final println to just test feed the target struct `Listings` with the values
println!("{}",types::Listings{ date_time: format!("{}", chrono::offset::Local::now()),
category: category.to_string(),
query: (&search_query).to_string(),
listings: listings.lock().unwrap() // It prevents me from owning this variable
}.to_json());
}

我偶然发现了错误

error[E0507]: cannot move out of `*thread` which is behind a mutable reference
--> src/main.rs:112:9
|
112 |         thread.join().unwrap();
|         ^^^^^^ move occurs because `*thread` has type `JoinHandle<()>`, which does not implement the `Copy` trait

它防止我在thread.join() for循环之后拥有变量。

当我尝试分配检查输出类型

let all_listings = listings.lock().unwrap()

all_listings报告一种类型的MutexGuard(这也是真实的线程内的循环,但它允许我调用向量方法),不允许我拥有的数据。我更改了Listings结构中的数据类型,以保存引用而不是拥有它。但似乎我在.to_json()中对结构体执行的操作要求我拥有它的值。ListingsStruct中listings的类型声明为Vec<Vec<Listing<T>>

然而,当我将.join().unwrap()移动到thread::spawn()块的末尾或应用于for循环内的句柄(同时禁用外部.join())时,此代码工作得很好。但是这使得所有的线程都在一个链中执行,这是不可取的,因为使用线程的主要目的是同时执行具有不同数据值的相同函数。

总的来说,我对Rust还是个新手(自从我使用它已经3周了),这是我第一次实现多线程。在此之前,我只在java和python中编写过单线程程序,所以如果可能的话,请对新手友好一些。然而,任何帮助都是感激的:).

我知道需要做什么了。首先,对于这种事情,我同意into_iter做你想要的,但在我看来,它模糊了为什么为什么是,当您借用它时,它不拥有该值,这对于JoinHandle<()>结构体上的join()方法是必要的。你会注意到它的签名是self,而不是&mut self或类似的东西。所以它需要真正的对象

要做到这一点,你需要把你的对象从Vec<thread::JoinHandle<()>>里面拿出来。如前所述,into_iter这样做是因为它"销毁"了";并接管现有的Vec,因此它完全拥有内容,并且迭代返回"实际的";要在没有副本的情况下连接的对象。但是您也可以使用remove一次拥有一个内容,如下所示:

while fetch_handle.len() > 0 {
let cur_thread = fetch_handle.remove(0); // moves it into cur_thread
cur_thread.join().unwrap();
}

这不是上面的for循环。如果你想尝试,可以在playground中找到完整的示例链接。

我希望这更清楚地说明了如何处理不能复制的东西,但是方法需要完全拥有它们,以及将它们从集合中取出的问题。想象一下,如果您只需要结束中的一个线程,并且您知道要结束哪个线程,但不想结束所有线程?Vec<_>::remove可以工作,但into_iter不行。

谢谢你问了一个让我思考的问题,并促使我自己去寻找答案(并尝试)。我还在学习Rust,所以这对我帮助很大。

编辑:

pop()while let的另一种方法:

while let Some(cur_thread) = fetch_handle.pop() {
cur_thread.join().unwrap();
}

这将从末尾穿过它(pop将其从末尾而不是前面拉出),但也不会通过将其从前面拉出来重新分配或移动向量内容。

好的,所以@PiRocks指出的问题似乎是在连接线程的for循环中。

for thread in fetch_handle.iter_mut() {
thread.join().unwrap();
}

问题是iter_mut()。使用into_iter()代替

for thread in fetch_handle.into_iter() {
thread.join().unwrap();
}

不产生错误,程序可以根据需要在线程间同时运行。

对此,@Kevin Anderson给出的解释是:

使用into_iter()会导致JoinHandle<()>进入for循环。

也查看文档(std::iter)我发现iter()iter_mut()self的引用上迭代into_iter()遍历self直接(拥有)。

所以iter_mut()是在&mut thread::JoinHandle<()>而不是thread::JoinHandle<()>上迭代。

最新更新