2017-08-15 47 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可能會滿足您的需求。

相關問題