2009-04-05 9 views
29

¿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.¿Hay una versión asincrónica de DirectoryInfo.GetFiles/Directory.GetDirectories en dotNet?

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

Respuesta

10

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 un lote más trabajo.

+6

¿Sigue siendo así con .NET 4.5 con un montón de nuevas API asíncronas? Estaba buscando esto y no lo vi. –

12

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, el archivo asíncrono, StreamReader , y las operaciones de 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) } 
+3

Esto no es cierto asincrónico de E/S, por supuesto, pero es una buena solución práctica, no obstante. Si realmente quieres operaciones verdaderamente asíncronas, tendrás que utilizar una horrible interoperabilidad de Win32, que supongo que no vale la pena en el contexto ... – Noldorin

+1

Además, estos días estoy usando F # en iOS a través de Xamarin (y pronto en Android), por lo que alguna interferencia de Win32 horrible ni siquiera es posible. –

0

la respuesta de la princesa es el camino a seguir para añadir granularidad entre las tareas - por lo este tipo de cosas dejaría oth er jugadores utilizan 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 pesado - 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 estaba llamadas de bajo nivel sólo un bonito envoltorio alrededor que expusieron algunas variantes asincrónicos. Algo así como BeginReadDir/EndReadDir.

+1

No pude encontrar la respuesta de Princess. –

+0

@ScottHutchinson Se ha ido, ni idea de cuándo se eliminó. (Mi pregunta es de 2009). Creo que fue esencialmente el mismo que el de Juliet, señalando que muchos de los operadores asíncronos no tienen un buen soporte de todos modos: van a consumir hilos y no a hacer algo sensato con los manipuladores de espera. –

+0

Supongo que Princess cambió su nombre a Juliet, cuyo perfil dice que ella es una "Alta Sacerdotisa", que en cierto modo es como una princesa. –

3

he utilizado este método para obtener objetos asíncronas de funciones/procedimientos, y siempre funcionaba muy bien:


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

no soy F # programador, 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 #.

+6

Esto es * flojo *, no * async * –

+0

Bueno, complételo en un método asíncrono/Tarea, ¿qué tiene usted? Simplemente empujando todo en un hilo diferente es una mala manera de lograr asincronía en mi humilde opinión. Sí, funciona en paralelo, pero es muy difícil terminar de manera determinista. –

+3

Rellenarlo en un método/tarea asíncrono no es lo mismo, y no está relacionado con la pereza. Estoy de acuerdo en que "insertarlo en un hilo diferente" es "mala asincrónica", debería usar IOCPs. –

3

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

+3

No realmente. Incluso devolver el primer archivo puede tomar docenas o cientos de milisegundos (piense en las rutas UNC). Todo ese tiempo tu hilo de ejecución está bloqueado. – MvdD

5

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

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

using Windows.Storage; 

... 

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

Usted fácilmente puede consumir estos de las aplicaciones tradicionales de escritorio .NET y la consola mediante el uso de la biblioteca UWP for Desktop por Luciano Wischik (de Microsoft).

Install-Package UwpDesktop 
+0

Internamente, ¿qué API usa esta biblioteca? ¿Llama a * true * -async (IO superpuesto?) Win32 funciones, o simplemente envuelve todo en un hilo? – Dai

Cuestiones relacionadas