multithreading - 无法从存储在向量中的 JoinHandles 加入线程 - Rust
问题描述
我正在编写一个程序,它从网站列表中抓取数据并将其存储到一个名为的结构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(在线程 for 循环中也是如此,但它允许我在其上调用向量方法)并且不允许我拥有数据。我更改了Listings
结构中的数据类型以保存引用而不是拥有它。但似乎我对结构执行的操作.to_json()
需要我拥有它的价值。Structlistings
内部的类型声明是.Listings
Vec<Vec<Listing<T>>
但是,当我将 移到块.join().unwrap()
的末尾thread::spawn()
或将其应用于 for 循环内的句柄(同时禁用 external .join()
)时,此代码可以正常工作。但这使得所有线程都在一条链中执行,这是不可取的,因为使用线程的主要目的是同时执行具有不同数据值的相同功能。
总的来说,我对 Rust 很陌生(我使用它已经 3 周了),这是我第一次实现多线程。在此之前,我只用 java 和 python 编写过单线程程序,所以如果可能的话,对菜鸟友好一点。然而,任何帮助表示赞赏:)。
解决方案
我弄清楚需要发生什么。首先,对于这种事情,我同意into_iter
做你想做的事,但 IMO 它掩盖了为什么。原因是当你借用它时,它不拥有该值,这是结构join()
上的方法所必需的JoinHandle<()>
。你会注意到它的签名需要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
上面的循环。如果您想尝试,请链接操场中的完整示例。
我希望这更清楚地说明如何处理无法复制的东西,但方法需要完全拥有它们,以及将它们从集合中取出的问题。想象一下,如果您只需要结束其中一个线程,并且您知道要结束哪个线程,但又不想全部结束? Vec<_>::remove
会工作,但into_iter
不会。
感谢您提出一个让我思考的问题,并促使我自己去查找答案(并尝试)。我还在学习 Rust,所以这很有帮助。
编辑:
pop()
使用and的另一种方法while let
:
while let Some(cur_thread) = fetch_handle.pop() {
cur_thread.join().unwrap();
}
这从末端穿过它(pop
将其从末端拉出,而不是从前面拉出),但也不会通过将矢量内容从前面拉出来重新分配或移动矢量内容。
推荐阅读
- google-bigquery - 如何选择没有别名的 bigquery 表的第 n 列?
- php - Laravel - 对于某些具有相同 mime 的文件,文件 mimetype 验证失败
- wordpress - 结帐期间的 Woocommerce update_status() ... 是否可以在电子邮件中包含转换 $note ?
- android - 列出奇数/偶数项目的两种不同设计
- java - 文件是文档对象吗?
- java - 尝试将 java spring 应用程序部署到 Oracle web 逻辑集群 java.lang.ArrayIndexOutOfBoundsException 时出错:52304
- c# - C# - 仅使用选项的字符串名称设置 CRM OptionSetValue
- javascript - 当 .then 方法未被调用时,Promise 是如何运行的?
- vba - 在两个不同的列中查找包含两个单元格值的行
- django - 如果用户被删除并且外键设置为models.SET_NULL,用户评论会发生什么