Partager via


Comment : itérer les répertoires de fichiers avec PLINQ

Cet exemple indique comment paralléliser des opérations sur des répertoires de fichiers. La première requête utilise la méthode GetFiles pour remplir un tableau de noms de fichiers dans un répertoire et tous les sous-répertoires. Cette méthode ne retourne aucun résultat avant que le tableau entier ne soit rempli, et par conséquent il peut introduire un problème de latence au début de l'opération. Cependant, une fois que le tableau est rempli, PLINQ peut le traiter en parallèle très rapidement.

La deuxième requête utilise les méthodes statiques EnumerateDirectories et EnumerateFiles qui démarrent en retournant les résultats immédiatement. Cette méthode peut être plus rapide lorsque vous itérez au sein d'arborescences de répertoires importantes, même si la durée de traitement comparée au premier exemple peut dépendre de nombreux facteurs.

Remarque AttentionAttention

Ces exemples sont destinés à montrer l'utilisation et peut ne pas s'exécuter plus rapidement que la requête LINQ to Objects séquentielle équivalente.Pour plus d'informations sur l'accélération, consultez Fonctionnement de l'accélération dans PLINQ.

Exemple

L'exemple suivant indique comment itérer au sein de répertoires de fichiers dans des scénarios simples lorsque vous avez accès à tous les répertoires de l'arborescence, que la taille des fichiers n'est pas très volumineuse et que les temps d'accès ne sont pas significatifs. Cette méthode implique une période de latence au début lorsque le tableau des noms de fichiers est en cours de création.


struct FileResult
{
    public string Text;
    public string FileName;
}
// Use Directory.GetFiles to get the source sequence of file names.
public static void FileIteration_1(string path)
{       
    var sw = Stopwatch.StartNew();
    int count = 0;
    string[] files = null;
    try
    {
        files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories);
    }
    catch (UnauthorizedAccessException e)
    {
        Console.WriteLine("You do not have permission to access one or more folders in this directory tree.");
        return;
    }

    catch (FileNotFoundException)
    {
        Console.WriteLine("The specified directory {0} was not found.", path);
    }

    var fileContents = from file in files.AsParallel()
            let extension = Path.GetExtension(file)
            where extension == ".txt" || extension == ".htm"
            let text = File.ReadAllText(file)
            select new FileResult { Text = text , FileName = file }; //Or ReadAllBytes, ReadAllLines, etc.              

    try
    {
        foreach (var item in fileContents)
        {
            Console.WriteLine(Path.GetFileName(item.FileName) + ":" + item.Text.Length);
            count++;
        }
    }
    catch (AggregateException ae)
    {
        ae.Handle((ex) =>
            {
                if (ex is UnauthorizedAccessException)
                {
                   Console.WriteLine(ex.Message);
                   return true;
                }
                return false;
            });
    }

    Console.WriteLine("FileIteration_1 processed {0} files in {1} milliseconds", count, sw.ElapsedMilliseconds);
    }

L'exemple suivant indique comment itérer au sein de répertoires de fichiers dans des scénarios simples lorsque vous avez accès à tous les répertoires de l'arborescence, que la taille des fichiers n'est pas très volumineuse et que les temps d'accès ne sont pas significatifs. Cette méthode commence à produire des résultats plus rapidement que dans l'exemple précédent.


struct FileResult
{
    public string Text;
    public string FileName;
}

// Use Directory.EnumerateDirectories and EnumerateFiles to get the source sequence of file names.
public static void FileIteration_2(string path) //225512 ms
{
    var count = 0;
    var sw = Stopwatch.StartNew();
    var fileNames = from dir in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories)
                    select dir;


    var fileContents = from file in fileNames.AsParallel() // Use AsOrdered to preserve source ordering
                       let extension = Path.GetExtension(file)
                       where extension == ".txt" || extension == ".htm"
                       let Text = File.ReadAllText(file)
                       select new { Text, FileName = file }; //Or ReadAllBytes, ReadAllLines, etc.
    try
    {
        foreach (var item in fileContents)
        {
            Console.WriteLine(Path.GetFileName(item.FileName) + ":" + item.Text.Length);
            count++;
        }
    }
    catch (AggregateException ae)
    {
        ae.Handle((ex) =>
            {
                if (ex is UnauthorizedAccessException)
                {
                   Console.WriteLine(ex.Message);
                   return true;
                }
                return false;
            });
    }

    Console.WriteLine("FileIteration_2 processed {0} files in {1} milliseconds", count, sw.ElapsedMilliseconds);
}

Si vous utilisez GetFiles, assurez-vous de disposer des autorisations suffisantes sur tous les répertoires de l'arborescence. Sinon, une exception est levée et aucun résultat n'est retourné. Si vous utilisez EnumerateDirectories dans une requête PLINQ, cela devient un problème de traiter les exceptions d'E/S de manière appropriée pour vous permettre de continuer les itérations. Si votre code doit traiter des exceptions d'E/S ou d'accès non autorisé, considérez la méthode décrite dans Comment : itérer les répertoires de fichiers avec la classe parallèle.

Si la latence d'E/S est un problème, par exemple avec l'E/S de fichier sur un réseau, envisagez d'utiliser l'une des techniques d'E/S asynchrone décrites dans Bibliothèque parallèle de tâches et programmation asynchrone .NET traditionnelle et dans cette publication de blog.

Voir aussi

Concepts

Parallel LINQ (PLINQ)

Historique des modifications

Date

Historique

Motif

Mai 2010

Remarque ajoutée concernant l'utilisation et l'accélération.

Commentaires client.