2009-04-05 16 views
29

Esiste una versione asincrona di DirectoryInfo.GetFiles/Directory.GetDirectories in dotNet? Mi piacerebbe usarli in un blocco Async F #, e sarebbe bello avere una versione che può essere chiamata con AsyncCallbacks.Esiste una versione asincrona di DirectoryInfo.GetFiles/Directory.GetDirectories in dotNet?

Problema è Sto cercando di eseguire l'operazione su un gruppo di directory, probabilmente su montature SMB su connessioni di rete lente, e non voglio un mucchio di thread thread thread seduti in attesa di letture di rete quando potrebbero fare altro lavoro.

risposta

10

No, non penso ci sia. L'approccio del pool thread è probabilmente il più pragmatico. In alternativa, suppongo che potresti passare a P/Invoke, ma quello sarebbe un lavoro .

+6

questo è ancora il caso ora che .NET 4.5 è fuori con un sacco di nuove API asincrone? Stavo cercando questo e non l'ho visto. –

12

non ho trovato una versione asincrona di GetFiles, se si guarda il codice sorgente per altre operazioni asincrone, sono definiti come segue:

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) 

In altre parole, il file Async, StreamReader , e le operazioni WebClient sono solo wrapper intorno alle operazioni sincroni, così si dovrebbe essere in grado di scrivere il proprio wrapper GetFiles/GetDirectories come segue:

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

Ovviamente questo non è vero I/O asincrono, ma è comunque una buona soluzione pratica. Se volevi operazioni veramente asincrone dovresti usare qualche orribile interpop di Win32, che immagino non valga la pena nel contesto ... – Noldorin

+1

Inoltre, in questi giorni sto usando F # su iOS tramite Xamarin (e prossimamente su Android), quindi alcune orribili interruzioni di Win32 non sono nemmeno possibili. –

0

risposta di Princess è la strada da percorrere per l'aggiunta di granularità tra le attività - così questo genere di cose lo farebbe fare giocatori er utilizzano il pool di 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"); 

Non aiuta tanto quando ognuno di quelle chiamate di blocco è pesante - e un GetFiles oltre SMB è praticamente la definizione di pesante.

Speravo ci fosse una sorta di equivalente per BeginRead/EndRead per le directory, e che GetFiles/GetDirectories ero chiamate solo una bella wrapper di livello inferiore che hanno esposto alcune varianti asincrone. Qualcosa come BeginReadDir/EndReadDir.

+1

Non sono riuscito a trovare la risposta di Princess. –

+0

@ScottHutchinson Non c'è più idea di quando è stato cancellato. (La mia domanda è da 2009). Penso che sia sostanzialmente la stessa di Juliet, sottolineando che molti operatori asincroni non hanno comunque un buon supporto: consumano thread e non fanno qualcosa di sensato con le maniglie di attesa. –

+0

Immagino che Princess abbia cambiato il suo nome in Juliet, il cui profilo dice che è una "High Priestess", che è una specie di principessa. –

3

Ho usato questo metodo per ottenere gli oggetti Async da funzioni/procedure, ed è sempre funzionava benissimo:


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

Non sono un F # programmatore, ma lo farei in 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); 
    } 
} 

Suppongo che ci sia un costrutto simile agli iteratori in F #.

+6

Questo è * pigro *, non * asincrono * –

+0

Bene, riempilo in un metodo asincrono/Attività, che cosa hai. Semplicemente spingendo tutto su un thread diverso è un brutto modo per ottenere l'asincronismo IMHO. Sì, funziona in parallelo, ma è molto difficile terminare in modo deterministico. –

+3

Riporlo in un metodo asincrono/attività non è la stessa, ed è indipendente dalla pigrizia. Sono d'accordo che "spingendolo in un thread diverso" è "brutto asincrono", dovrebbe utilizzare IOCP. –

3

In realtà, secondo lo help for Directory.GetFiles, Directory.EnumerateFiles restituirà immediatamente il primo risultato (è un IEnumerable), anziché attendere l'intero elenco prima di tornare. Credo che sia probabilmente quello che stai cercando.

+3

Non proprio. Anche restituire il primo file può richiedere dozzine o centinaia di millisecondi (si pensi ai percorsi UNC). Per tutto il tempo il tuo thread di esecuzione è bloccato. – MvdD

5

Questo può essere considerato un po 'un trucco, ma si potrebbe prendere in considerazione l'utilizzo di UWP StorageFolder API.

C# esempio (anche se F # è probabilmente altrettanto facile):

using Windows.Storage; 

... 

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

potete facilmente può consumare questi da applicazioni desktop .NET e console tradizionali utilizzando la libreria UWP for Desktop da Luciano Wischik (di Microsoft).

Install-Package UwpDesktop 
+0

Internamente, che API usa questa libreria? Chiama * true * -async (sovrapposte IO?) Funzioni Win32 o semplicemente avvolge tutto in una discussione? – Dai