有什么办法来实现ZipFile的Send特性?
我想通过使用zip crate在不同的线程中读取.zip
文件。有什么办法来实现ZipFile的Send特性?
extern crate zip;
use zip::ZipArchive;
use zip::read::ZipFile;
use std::fs::File;
use std::io::BufReader;
use std::thread;
fn compute_hashes(mut file: ZipFile) {
let reader_thread= thread::spawn(move || {
let mut reader = BufReader::new(file);
/* ... */
});
}
fn main() {
let mut file = File::open(r"facebook-JakubOnderka.zip").unwrap();
let mut zip = ZipArchive::new(file).unwrap();
for i in 0..zip.len() {
let mut inside = zip.by_index(i).unwrap();
if !inside.name().ends_with("/") { // skip directories
println!("Filename: {}", inside.name());
compute_hashes(inside);
}
}
}
但是,编译器显示我这个错误:
error[E0277]: the trait bound `std::io::Read: std::marker::Send` is not satisfied
--> src/main.rs:10:24
|
10 | let reader_thread= thread::spawn(move || {
| ^^^^^^^^^^^^^ `std::io::Read` cannot be sent between threads safely
|
= help: the trait `std::marker::Send` is not implemented for `std::io::Read`
= note: required because of the requirements on the impl of `std::marker::Send` for `&mut std::io::Read`
= note: required because it appears within the type `std::io::Take<&mut std::io::Read>`
= note: required because it appears within the type `zip::crc32::Crc32Reader<std::io::Take<&mut std::io::Read>>`
= note: required because it appears within the type `zip::read::ZipFileReader<'_>`
= note: required because it appears within the type `zip::read::ZipFile<'_>`
= note: required because it appears within the type `[[email protected]/main.rs:10:38: 13:6 file:zip::read::ZipFile<'_>]`
= note: required by `std::thread::spawn`
但对于类型std::fs::File
相同的作品。是否有必要修复zip
箱子或有任何其他方法?
这是zip
条板箱的API的限制,您无法真正改变任何内容。问题是ZipArchive
是通过调用new
并传递一个阅读器创建的 - 实现了Read
和Seek
。但这些是读者的唯一要求(特别是,读者不需要是Clone
)。因此,整个ZipArchive
只能拥有一个阅读器。
但是现在ZipArchive
能够生产ZipFile
自己实现Read
自己。如果整个ZipArchive
只有一个阅读器,这是如何工作的?通过分享!唯一的读者是在档案和所有文件之间共享的。但是这个共享不是线程保存!每个ZipFile
存储一个可读的读者参考 - 这违反了Rust的核心原则。
这是一个已知的箱子问题,正在讨论on the GitHub issue tracker。
那么你现在可以做什么?不是一大堆,但(由库的作者提到的)几种可能性可能是你的使用情况确定:
-
你可以先解压缩整个文件到内存中,然后将原始数据发送到另一个线程对它进行计算。喜欢的东西:
let data = Vec::new(); BufReader::new(file).read_to_end(&mut data)?; let reader_thread= thread::spawn(move || { // Do stuff with `data` });
但如果你只是想计算上的所有文件便宜的哈希函数,内容加载到内存中,可能比计算在飞行中的散列慢,如果你的文件有很大的可能是不可行的。
为每个线程创建一个
ZipArchive
。如果你在压缩文件有许多小文件,这可能会很慢...
一个小小的提示:启动一个线程花费时间。您通常不希望为每个工作单元启动线程,而是在线程池中维护固定数量的线程,管理队列中的工作并将工作分配给空闲的工作线程。 The threadpool
crate可能会满足您的需求。