Is there an async version of DirectoryInfo.GetFiles / Directory.GetDirectories in dotNet?

前端 未结 7 2100
北荒
北荒 2020-12-08 06:50

Is there an asynchronous version of DirectoryInfo.GetFiles / Directory.GetDirectories in dotNet? I\'d like to use them in an F# async block, and it\'d be nice to have a ver

相关标签:
7条回答
  • 2020-12-08 07:01

    I didn't find an async version of GetFiles, however if you look at the sourcecode for other Async operations, they're defined as follows:

    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 other words, the Async file, streamreader, and WebClient operations are just wrappers around the syncronous operations, so you should be able to write your own wrapper around GetFiles/GetDirectories as follows:

    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) }
    
    0 讨论(0)
  • 2020-12-08 07:07

    This may be considered a bit of a hack, but you might consider using the UWP StorageFolder API.

    C# example (though F# is probably just as easy):

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

    You can easily consume these from traditional .NET desktop and console applications by using the UWP for Desktop library by Lucian Wischik (of Microsoft).

    Install-Package UwpDesktop
    
    0 讨论(0)
  • 2020-12-08 07:07

    Actually, according to the help for Directory.GetFiles, Directory.EnumerateFiles will return the first result immediately (it's an IEnumerable), rather than wait for the entire list before returning. I believe that's probably what you're looking for.

    0 讨论(0)
  • 2020-12-08 07:12

    I am no F# programmer, but I'd do this 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);
        }
    }
    

    I'm assuming there's a similar construct to iterators in F#.

    0 讨论(0)
  • 2020-12-08 07:14

    Princess's answer is the way to go for adding granularity between tasks - so this kind of thing would let other players use the 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");
    

    It doesn't help as much when each one of those blocking calls is heavy - and a GetFiles over SMB is pretty much the definition of heavy.

    I was hoping there was some sort of equivalent for BeginRead/EndRead for directories, and that GetFiles/GetDirectories was just a nice wrapper around lower-level calls that exposed some async variants. Something like BeginReadDir/EndReadDir.

    0 讨论(0)
  • 2020-12-08 07:18

    No, I don't think there is. The pool thread approach is probably the most pragmatic. Alternatively, I guess you could drop down to P/Invoke - but that would be a lot more work.

    0 讨论(0)
提交回复
热议问题