我正在编写一个程序,该程序从网站列表中抓取数据并将其存储到一个名为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()
中对结构体执行的操作要求我拥有它的值。Listings
Struct中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<()>
上迭代。