.net f# asynchronous directoryinfo async-workflow

.net - directory c#



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

De hecho, de acuerdo con la ayuda de 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.

¿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.


Esto puede considerarse un truco, pero podría considerar usar la API de UWP StorageFolder .

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 aplicaciones tradicionales de escritorio y consola .NET utilizando la biblioteca UWP for Desktop de Lucian Wischik (de Microsoft).

Install-Package UwpDesktop


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)


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 heavy .

Esperaba que hubiera algún tipo de equivalente para BeginRead / EndRead para directorios, y que GetFiles / GetDirectories fuera solo una buena envoltura alrededor de llamadas de nivel inferior que expusiera algunas variantes de sincronización. Algo así como BeginReadDir / EndReadDir .


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


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 #.


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