无法从存储在 Vector 中的 JoinHandles 连接线程 - Rust

Rak*_*tty 6 multithreading rust

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

\n
use std::{ thread,\n           sync::{ Arc, Mutex }\n         };\n\nfn main() {\n    // ... some declarations\n    let sites_count = site_list.len(); // site_list is a vector containing the list of websites\n\n    // The variable to be updated by the thread instances ( `Listing` is a struct holding the information ) \n    let listings: Arc<Mutex<Vec<Vec<types::Listing<String>>>>> = Arc::new(Mutex::new(Vec::new()));\n\n    // A vector containing all the JoinHandles for the spawned threads\n    let mut fetch_handle: Vec<thread::JoinHandle<()>> = Vec::new();\n\n    // Spawn a thread for each concurrent website\n    for i in 0..sites_count { \n        let slist = Arc::clone(&site_list);\n        let listng = Arc::clone(&listings);\n        fetch_handle.push(\n            thread::spawn(move || {\n                println!("\xe2\x8c\x9b Spawned Thread: {}",i);\n                let site_profile = read_profile(&slist[i]);\n                let results = function1(function(2)) // A long list of functions from a submodule that make the http request and parse the data into `Listing`\n                listng.lock().unwrap().push(results);\n            }));\n    }\n    \n    for thread in fetch_handle.iter_mut() { \n        thread.join().unwrap();\n    }\n\n    // This is the one line version of the above for loop - yields the same error.\n    // fetch_handle.iter().map(|thread| thread.join().unwrap()); \n\n    // The final println to just test feed the target struct `Listings` with the values\n    println!("{}",types::Listings{ date_time: format!("{}", chrono::offset::Local::now()),\n                                   category: category.to_string(),\n                                   query: (&search_query).to_string(),\n                                   listings: listings.lock().unwrap() // It prevents me from owning this variable\n                                 }.to_json());\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n

我偶然发现了这个错误

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

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

\n

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

\n
let all_listings = listings.lock().unwrap()\n
Run Code Online (Sandbox Code Playgroud)\n

all_listings报告一种 MutexGuard(在线程 for 循环中也是如此,但它允许我在其上调用向量方法)并且不允许我拥有数据。\n我更改了结构中的数据类型Listings以保存参考而不是拥有它。但看来我对结构执行的操作要求.to_json()我拥有它的值。\n结构listings内部的类型声明ListingsVec<Vec<Listing<T>>

\n

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

\n

总的来说,我对 Rust 还很陌生(自从我使用它以来已经 3 周了),这是我第一次实现多线程。在此之前,我只用 java 和 python 编写过单线程程序,所以如果可能的话,对菜鸟友好一些。不过,如有任何帮助,我们将不胜感激:)。

\n

Kev*_*son 10

我知道需要发生什么。首先,对于这种事情,我同意做into_iter你想做的事,但在我看来,它掩盖了原因。原因是当您借用它时,它并不拥有该值,而该值对于结构join()上的方法来说是必需的JoinHandle<()>。你会注意到它的签名 takeself和 not&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();
}
Run Code Online (Sandbox Code Playgroud)

这代替了for上面的循环。如果您想尝试的话,可以链接游乐场中的完整示例

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

感谢您提出了一个让我思考的问题,并促使我自己去查找答案(并尝试一下)。我还在学习 Rust,所以这很有帮助。

编辑:

另一种使用pop()and 的方法while let

while let Some(cur_thread) = fetch_handle.pop() {
    cur_thread.join().unwrap();
}
Run Code Online (Sandbox Code Playgroud)

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

  • 我个人会使用“for cur_thread in fetch_handle.drain(..)”。但通往罗马的方式有很多种。 (3认同)
  • 这比我自己的答案更清楚。我会接受这个答案。 (2认同)