【发布时间】:2021-08-28 18:00:23
【问题描述】:
我正在编写一个程序,它从网站列表中抓取数据并将其存储到一个名为 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() 中的结构执行的操作要求我拥有它的价值。
listings 在 Listings 结构中的类型声明是 Vec<Vec<Listing<T>>。
但是,当我将 .join().unwrap() 移动到 thread::spawn() 块的末尾或将其应用于 for 循环内的句柄(同时禁用外部 .join() )时,此代码可以正常工作。但这使得所有线程都在一条链中执行,这是不可取的,因为使用线程的主要目的是同时执行具有不同数据值的相同功能。
总的来说,我对 Rust 还是很陌生(我使用它已经 3 周了),这是我第一次实现多线程。在此之前,我只用 java 和 python 编写过单线程程序,所以如果可能的话,对菜鸟友好一点。然而,任何帮助表示赞赏:)。
【问题讨论】:
-
把
for thread in fetch_handle.iter_mut() {改成for thread in fetch_handle.into_iter() { -
我没有足够的经验来弄清楚你必须做什么,但@PiRocks 的建议可能会奏效,因为
JoinHandle<()>然后完全移入当前迭代。您的方式可能不起作用,因为当您join和unwrap(我不确定哪个部分更重要)时,向量中还剩下什么?这是我自己不确定的生命周期,但无论如何,这可能是错误的“来源”。into_iter“破坏”原始向量,允许在加入后明确所有权。我不确定做你想做的事情的正确方法。 -
只要
for thread in fetch_handle应该可以工作,因为Vec实现了IntoIterator。单行版只需要into_iter()。 -
另见How to take ownership of T from Arc<Mutex<T>>?你可能不想在最后使用
.lock().unwrap(),因为lock借用了互斥锁内的项目而不是将其移出(就像into_inner一样) . -
@PiRocks 是的,这似乎完全解决了问题。 @Kevin 好的。我还没有进入任何 into* 方法。我还不完全明白它的作用,但我会查一下。 @trentcl 我在单行版本中尝试了
into_iter(),但它弹出警告Unused Map that must be used, iterators are lazy and do nothing until consumed。 @trentcl 好的。因此,仅在线程内使用.lock().unwrap()并在不再需要互斥锁时使用try_unwrap()和.into_inner()组合,明白了。谢谢@PiRocks @Kevin 和 @trentcl !
标签: multithreading rust