2009-04-05 13 views
29

Gibt es in dotNet eine asynchrone Version von DirectoryInfo.GetFiles/Directory.GetDirectories? Ich möchte sie in einem Async-Block F # verwenden, und es wäre nett, eine Version zu haben, die mit AsyncCallbacks aufgerufen werden kann.Gibt es in dotNet eine asynchrone Version von DirectoryInfo.GetFiles/Directory.GetDirectories?

Problem ist, ich versuche, eine Reihe von Verzeichnissen, wahrscheinlich auf SMB Mounts über langsame Netzwerkverbindungen zu saugen, und ich möchte nicht eine Reihe von Thread-Pool-Threads herumstehen warten auf Netzwerk liest, wenn sie tun könnten andere Arbeit.

Antwort

10

Nein, das glaube ich nicht. Der Pool-Thread-Ansatz ist wahrscheinlich der pragmatischste. Alternativ, ich denke, Sie könnten auf P/Invoke fallen - aber das wäre ein Los mehr Arbeit.

+6

ist dies immer noch der Fall, dass .NET 4.5 mit einer Reihe neuer asynchroner APIs auskommt? Ich habe danach gesucht und es nicht gesehen. –

12

ich keine Asynchron-Version von GetFiles gefunden haben, aber wenn Sie für andere Async Operationen am Quelltext aussehen, sind sie wie folgt definiert:

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) 

Mit anderen Worten, die Async-Datei, Stream und WebClient Operationen sind nur Wrapper um die syncronous Operationen, so sollten Sie in der Lage sein, Ihren eigenen Wrapper um GetFiles/GetDirectories zu schreiben, wie folgt:

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

Das ist natürlich nicht richtig async I/O, aber es ist trotzdem eine gute praktische Lösung. Wenn Sie wirklich asynchrone Operationen wollten, müssten Sie ein schreckliches Win32 Interop verwenden, was ich mir vorstellen würde, ist es im Kontext nicht wert ... – Noldorin

+1

Außerdem benutze ich heutzutage F # auf iOS über Xamarin (und bald auf Android), so dass einige schreckliche Win32-Interop nicht einmal möglich ist. –

0

Princess Antwort ist die Art und Weise für das Hinzufügen von Granularität zwischen Aufgaben zu gehen - so diese Art von Sache würde sich lohnen er Spieler verwenden, um den Thread-Pool:

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

Es nicht so viel helfen, wenn jeder dieser blockierende Aufrufe ist schwer - und ein GetFiles über SMB ist so ziemlich die Definition von schweren.

Ich hatte gehofft, es war eine Art Äquivalent für BeginRead/EndRead für Verzeichnisse und dass GetFiles/GetDirectories war nur eine nette Wrapper um niedrigerer Ebene Anrufe, die einige Asynchron-Varianten ausgesetzt. Etwas wie BeginReadDir/EndReadDir.

+1

Ich konnte die Antwort von Prinzessin nicht finden. –

+0

@ScottHutchinson Es ist weg, keine Ahnung, wann es gelöscht wurde. (Meine Frage ist von 2009). Ich denke, es war im Wesentlichen dasselbe wie das von Julia und wies darauf hin, dass viele der asynchronen Operatoren sowieso keine gute Unterstützung haben - sie werden Threads konsumieren und nicht etwas Sinnvolles mit Wartegriffen machen. –

+0

Ich vermute, dass Prinzessin ihren Namen in Julia geändert hat, deren Profil sagt, dass sie eine "Hohepriesterin" ist, die irgendwie wie eine Prinzessin ist. –

3

Ich habe mehrmals diesen Ansatz verwendet, um Async Objekte von Funktionen/Prozeduren zu erhalten, und es funktionierte immer gut:


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

Ich bin kein F # Programmierer, aber ich würde dies in C# tun:

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

Ich nehme an, dass es ein ähnliches Konstrukt zu Iteratoren in F # gibt.

+6

Das ist * faul *, nicht * async * –

+0

Nun, stopfen Sie es in einer asynchronen Methode/Aufgabe, was hast du. Indem man einfach alles auf einen anderen Thread verschiebt, ist ein schlechter Weg, um IMHO-Asynchronität zu erreichen. Ja, es läuft parallel, aber es ist sehr schwierig, deterministisch zu beenden. –

+3

Füllung in einer asynchronen Methode/Aufgabe ist nicht das gleiche und hat nichts mit Faulheit zu tun. Ich stimme zu, dass "es in einen anderen Thread drängen" ist "schlecht async", sollte es IOCPs verwenden. –

3

Eigentlich, nach der help for Directory.GetFiles, wird Directory.EnumerateFiles das erste Ergebnis sofort zurückgeben (es ist ein IEnumerable), anstatt auf die gesamte Liste vor der Rückkehr warten. Ich glaube, das ist wahrscheinlich das, wonach Sie suchen.

+3

Nicht wirklich. Selbst das Zurückgeben der ersten Datei kann Dutzende oder Hunderte von Millisekunden dauern (denke an UNC-Pfade). Die ganze Zeit ist dein Ausführungsthread blockiert. – MvdD

5

Dies kann als ein bisschen ein Hack betrachtet werden, aber Sie könnten die Verwendung der UWP StorageFolder API in Betracht ziehen.

C# Beispiel (obwohl F # wahrscheinlich genauso einfach ist):

using Windows.Storage; 

... 

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

Sie können ganz einfach verbrauchen diese von Anwendungen traditionellen .NET Desktop und Konsole der UWP for Desktop Bibliothek von Lucian Wischik unter Verwendung von (von Microsoft).

Install-Package UwpDesktop 
+0

Intern, welche API verwendet diese Bibliothek? Ruft es * true * -async (überlappende IO?) Win32-Funktionen auf, oder wickelt einfach alles in einen Thread ein? – Dai