2017-08-15 1 views
3

Je veux lire un fichier .zip dans un fil différent en utilisant le zip crate.Est-il possible d'implémenter l'attribut Envoyer pour ZipFile?

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); 
     } 
    } 
} 

Mais le compilateur me montre cette erreur:

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` 

Mais les mêmes œuvres pour le type std::fs::File. Est-il nécessaire de fixer la caisse zip ou existe-t-il une autre méthode?

Répondre

4

Ceci est une limitation de l'API de zip crate et vous ne pouvez pas vraiment changer quoi que ce soit. Le problème est que le fichier ZipArchive est créé en appelant new et en passant un lecteur - quelque chose qui implémente Read et Seek. Mais ce sont les seules exigences pour le lecteur (en particulier, le lecteur n'a pas besoin d'être Clone). Ainsi, l'ensemble ZipArchive ne peut posséder qu'un seul lecteur.

Mais maintenant, le ZipArchive est en mesure de produire ZipFile s qui mettent en œuvre Read eux-mêmes. Comment cela fonctionne-t-il si l'ensemble ZipArchive a seulement un lecteur? Par le partage! Le seul lecteur est partagé entre l'archive et tous les fichiers. Mais ce partage n'est pas thread sauver! Une référence mutable au lecteur est stockée dans chaque ZipFile - ceci viole le principe de base de Rust.

Ceci est un problème connu de la caisse et est en cours de discussion on the GitHub issue tracker.


Alors, que pouvez-vous faire maintenant? Pas beaucoup, mais quelques possibilités (comme mentionné par l'auteur de la bibliothèque) pourrait être OK pour votre cas d'utilisation:

  • Vous pouvez décompresser le fichier en mémoire, puis envoyer les données brutes à un autre fil faire des calculs à ce sujet. Quelque chose comme:

    let data = Vec::new(); 
    BufReader::new(file).read_to_end(&mut data)?; 
    let reader_thread= thread::spawn(move || { 
        // Do stuff with `data` 
    }); 
    

    Mais si vous voulez juste pour calculer une fonction de hachage pas cher sur tous les fichiers, charger le contenu dans la mémoire est probablement plus lent que le calcul du hachage à la volée et peut-être infaisable si vos fichiers sont grandes.

  • Création d'un ZipArchive pour chaque thread. Cela pourrait être très lent si vous avez beaucoup de petits fichiers dans vos archives ...


Un petit indice: à partir d'un fil coûte du temps. Souvent, vous ne souhaitez pas démarrer un thread pour chaque unité de travail, mais plutôt maintenir un nombre fixe de threads dans un pool de threads, gérer le travail dans une file d'attente et affecter le travail aux threads de travail inactifs. The threadpool crate pourrait servir vos besoins.