Pregunta ¿Hay una versión asincrónica de DirectoryInfo.GetFiles / Directory.GetDirectories en dotNet?


¿Hay una versión asincrónica de DirectoryInfo.GetFiles / Directory.GetDirectories en dotNet? Me gustaría utilizarlos en un bloque asíncrono F #, y sería bueno tener una versión que se pueda llamar con AsyncCallbacks.

El problema es que estoy tratando de absorber un montón de directorios, probablemente en montajes SMB sobre conexiones de red lentas, y no quiero que haya un montón de subprocesos de subprocesos esperando la lectura de la red cuando podrían estar haciendo otro trabajo.


32
2018-04-05 14:40


origen


Respuestas:


No, no creo que exista. El enfoque del hilo de la piscina es probablemente el más pragmático. Alternativamente, supongo que podrías bajar a P / Invocar, pero eso sería un mucho mas trabajo.


11
2018-04-05 14:52



No encontré una versión asincrónica de GetFiles; sin embargo, si miras el código fuente de otras operaciones Async, se definen de la siguiente manera:

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 otras palabras, las operaciones de archivo Async, lector de secuencias y WebClient son solo envoltorios de las operaciones sincrónicas, por lo que debería poder escribir su propio contenedor en GetFiles / GetDirectories de la siguiente manera:

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
2018-04-05 14:57



Esto puede considerarse un truco, pero podrías considerar usar UWP StorageFolder API.

Ejemplo de C # (aunque F # probablemente sea igual de fácil):

using Windows.Storage;

...

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

Puede consumir fácilmente estos desde las aplicaciones de escritorio y consola .NET tradicionales utilizando el UWP para escritorio biblioteca de Lucian Wischik (de Microsoft).

Install-Package UwpDesktop

5
2018-06-21 18:07



He utilizado varias veces este enfoque para obtener objetos Async a partir de funciones / procedimientos, y siempre funcionó de maravilla:


let AsyncGetDirectories path = 
    let fn = new Func<_, _>(System.IO.Directory.GetDirectories)
    Async.BuildPrimitive(path, fn.BeginInvoke, fn.EndInvoke)

3
2018-04-05 18:49



En realidad, de acuerdo con el Ayuda para Directory.GetFiles, Directory.EnumerateFiles devolverá el primer resultado de inmediato (es un IEnumerable), en lugar de esperar toda la lista antes de volver. Creo que eso es probablemente lo que estás buscando.


3
2018-01-26 14:25



Posiblemente vea también

http://weblogs.asp.net/podwysocki/archive/2009/03/18/functional-net-laziness-becomes-you.aspx


1
2018-04-05 17:27



No soy programador de F #, pero haría esto 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);
    }
}

Supongo que hay una construcción similar a los iteradores en F #.


1
2018-01-01 21:24



La respuesta de Princess es el camino a seguir para agregar granularidad entre las tareas, por lo que este tipo de cosas permitiría que otros jugadores usen el grupo de subprocesos:

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

No ayuda mucho cuando cada una de esas llamadas de bloqueo es pesada, y un GetFiles sobre SMB es más o menos la definición de pesado.

Esperaba que hubiera algún tipo de equivalente para BeginRead/EndRead para directorios, y eso GetFiles/GetDirectories era solo una buena envoltura alrededor de las llamadas de bajo nivel que exponían algunas variantes de sincronización. Algo como BeginReadDir/EndReadDir.


0
2018-04-05 16:46