2017-08-15 19 views
3

我想通过使用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箱子或有任何其他方法?

回答

4

这是zip条板箱的API的限制,您无法真正改变任何内容。问题是ZipArchive是通过调用new并传递一个阅读器创建的 - 实现了ReadSeek。但这些是读者的唯一要求(特别是,读者不需要是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可能会满足您的需求。

相关问题