Existe-t-il une version asynchrone de DirectoryInfo?GetFiles / Directory.GetDirectories in dotNet?

Existe-t-il une version asynchrone de DirectoryInfo?GetFiles / Directory.GetDirectories in dotNet? J'aimerais les utiliser dans un bloc F# async, et ce serait bien d'avoir une version qui peut être appelée avec des Asyncallbacks.

le problème c'est que j'essaie d'aspirer un tas de répertoires, probablement sur des montages SMB sur des connexions réseau lentes, et je ne veux pas d'un tas de threads de piscine assis autour en attendant des lectures réseau alors qu'ils pourraient faire d'autres travaux.

33
demandé sur Noldorin 2009-04-05 18:40:19

8 réponses

Non, je ne pense pas qu'il y est. L'approche du "fil rouge" est probablement la plus pragmatique. Alternativement, je suppose que vous pourriez tomber à P / Invoke-mais ce serait un beaucoup plus de travail.

11
répondu Marc Gravell 2009-04-05 14:52:28

Je n'ai pas trouvé de version async de GetFiles, cependant si vous regardez le code source pour d'autres opérations Async, elles sont définies comme suit:

module FileExtensions =

        let UnblockViaNewThread f =
            async { //let ctxt = System.Threading.SynchronizationContext.Current
                    do! Async.SwitchToNewThread ()
                    let res = f()
                    do! Async.SwitchToThreadPool ()
                    //do! Async.SwitchTo ctxt
                    return res }

        type System.IO.File with
            static member AsyncOpenText(path)   = UnblockViaNewThread (fun () -> System.IO.File.OpenText(path))
            static member AsyncAppendText(path) = UnblockViaNewThread (fun () -> System.IO.File.AppendText(path))
            static member AsyncOpenRead(path)   = UnblockViaNewThread (fun () -> System.IO.File.OpenRead(path))
            static member AsyncOpenWrite(path)  = UnblockViaNewThread (fun () -> System.IO.File.OpenWrite(path))
            static member AsyncOpen(path,mode,?access,?share) =
                let access = match access with Some v -> v | None -> System.IO.FileAccess.ReadWrite
                let share = match share with Some v -> v | None -> System.IO.FileShare.None
                UnblockViaNewThread (fun () -> System.IO.File.Open(path,mode,access,share))

            static member OpenTextAsync(path)   = System.IO.File.AsyncOpenText(path)
            static member AppendTextAsync(path) = System.IO.File.AsyncAppendText(path)
            static member OpenReadAsync(path)   = System.IO.File.AsyncOpenRead(path)
            static member OpenWriteAsync(path)  = System.IO.File.AsyncOpenWrite(path)
            static member OpenAsync(path,mode,?access,?share) = System.IO.File.AsyncOpen(path, mode, ?access=access, ?share=share)

en d'autres termes, les opérations Async, streamreader et WebClient ne sont que des enveloppements autour des opérations syncrones, donc vous devriez être capable d'écrire votre propre enveloppement autour de GetFiles/GetDirectories comme suit:

module IOExtensions =
    type System.IO.Directory with
        static member AsyncGetFiles(directory) = async { return System.IO.Directory.GetFiles(directory) }
        static member AsyncGetDirectories(path) = async { return System.IO.Directory.GetDirectories(path) }
12
répondu Juliet 2009-04-05 15:31:52

Cela peut être considéré comme un peu un hack, mais vous pouvez envisager d'utiliser le UWP StorageFolder API.

exemple en C# (si F# est probablement tout aussi simple):

using Windows.Storage;

...

var folder = await StorageFolder.GetFolderFromPathAsync(path);
var files = await folder.GetFilesAsync();
var folders = await folder.GetFoldersAsync();

vous pouvez facilement les consommer à partir des applications traditionnelles .net desktop et console en utilisant le UWP pour les pc de Bureau bibliothèque de Lucian Wischik (de Microsoft).

Install-Package UwpDesktop
5
répondu Matt Johnson 2016-06-21 18:07:10

j'ai utilisé plusieurs fois cette approche pour obtenir des objets asynchrones à partir de fonctions / procédures, et ça a toujours bien fonctionné:


let AsyncGetDirectories path = 
    let fn = new Func<_, _>(System.IO.Directory.GetDirectories)
    Async.BuildPrimitive(path, fn.BeginInvoke, fn.EndInvoke)
3
répondu em70 2009-04-05 18:49:10

en fait, selon le aide Directory.GetFiles,Directory.EnumerateFiles retourner le premier résultat immédiatement (c'est un IEnumerable), plutôt que d'attendre toute la liste avant de revenir. Je crois que c'est probablement ce que vous cherchez.

3
répondu moswald 2014-01-26 14:25:30

je ne suis pas F# programmeur, mais je préfère le faire en C#:

static IEnumerable<string> IterateFiles(string path, string pattern) {
    var entryQueue = new Queue<string>();
    entryQueue.Enqueue(path);

    while (entryQueue.Count > 0) {
        var subdirs = Directory.GetDirectories(entryQueue.Peek());
        var files = Directory.GetFiles(entryQueue.Peek(), pattern, SearchOption.TopDirectoryOnly);
        foreach (var file in files)
            yield return file;
        entryQueue.Dequeue();

        foreach(var subdir in subdirs)
            entryQueue.Enqueue(subdir);
    }
}

je suppose qu'il y a une construction similaire aux itérateurs dans F#.

0
répondu Jörgen Sigvardsson 2013-01-01 21:24:33

la réponse de Princess est la façon d'ajouter la granularité entre les tâches - donc ce genre de chose permettrait aux autres joueurs d'utiliser le pool de thread:

let! x = OpenTextAsync("whatever");
// opening for something else to run
let! x = OpenTextAsync("whatever");
// opening for something else to run
let! x = OpenTextAsync("whatever");

cela n'aide pas autant quand chacun de ces appels de blocage est lourd - et un GetFiles sur SMB est à peu près la définition de lourd.

j'espérais qu'il y avait une sorte d'équivalent de BeginRead/EndRead pour les répertoires, et que GetFiles/GetDirectories était juste un wrapper autour de nice des appels de niveau inférieur qui ont exposé des variantes d'async. Quelque chose comme BeginReadDir/EndReadDir.

0
répondu James Moore 2013-09-27 14:43:10