Pregunta

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

El problema es que estoy tratando de absorber un montón de directorios, probablemente en montajes SMB a través de conexiones de red lentas, y no quiero un montón de subprocesos de grupo de subprocesos esperando lecturas de red cuando podrían estar haciendo otro trabajo.

¿Fue útil?

Solución

No, no creo que haya. El enfoque hilo piscina es probablemente el más pragmático. Por otra parte, supongo que se podría bajar a P / Invoke -. Pero eso sería un mucho más trabajo

Otros consejos

no he encontrado una versión asíncrona de GetFiles, sin embargo, si nos fijamos en el código fuente para otras operaciones asíncronas, que están definidos 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, los archivos asíncrono, StreamReader y operaciones WebClient son sólo las envolturas alrededor de las operaciones síncrono, por lo que debe ser capaz de escribir su propia envoltura alrededor 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) }

Esto puede ser considerado como un poco de un truco, pero es posible considerar el uso de la UWP StorageFolder API .

C # ejemplo (aunque F # es probablemente igual de fácil):

using Windows.Storage;

...

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

Se puede consumir fácilmente estos de las aplicaciones tradicionales de escritorio .NET y de la consola utilizando el uwp de escritorio biblioteca de Lucian Wischik (de Microsoft).

Install-Package UwpDesktop

En realidad, de acuerdo con el href="http://msdn.microsoft.com/en-us/library/07wt70x2%28v=vs.110%29.aspx" para Directory.GetFiles , Directory.EnumerateFiles devolverá el primer resultado inmediato (es un IEnumerable), en lugar de esperar a que toda la lista antes de regresar. Creo que eso es probablemente lo que estás buscando.

He usado varias veces este enfoque para obtener objetos asíncronas de funciones / procedimientos, y siempre funcionó muy bien:


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

No soy F # programador, pero me gustaría hacer 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);
    }
}

Estoy asumiendo que hay una construcción similar a los iteradores en C #.

La respuesta de la princesa es el camino a seguir para añadir granularidad entre las tareas - por lo que este tipo de cosas permitiría que otros jugadores utilizar 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 tanto cuando cada una de esas llamadas de bloqueo es pesada -. Y un GetFiles más de SMB es más o menos la definición de pesada

Tenía la esperanza de que había algún tipo de equivalente para BeginRead / EndRead para los directorios, y que GetFiles / GetDirectories era sólo un bonito envoltorio alrededor de llamadas de bajo nivel que expusieron algunas variantes asincrónicos. Algo así como BeginReadDir / EndReadDir.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top