53

dotNet 中是否有 DirectoryInfo.GetFiles / Directory.GetDirectories 的异步版本?我想在 F# 异步块中使用它们,并且有一个可以用 AsyncCallbacks 调用的版本会很好。

问题是我试图吸入一堆目录,可能是通过慢速网络连接安装在 SMB 上,我不希望一堆线程池线程在他们可以做其他工作时等待网络读取。

4

7 回答 7

14

我没有找到 GetFiles 的异步版本,但是如果您查看其他异步操作的源代码,它们的定义如下:

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)

换句话说,Async 文件、streamreader 和 WebClient 操作只是同步操作的包装器,因此您应该能够编写自己的 GetFiles/GetDirectories 包装器,如下所示:

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) }
于 2009-04-05T14:57:00.433 回答
13

不,我认为没有。池线程方法可能是最实用的。或者,我想你可以下拉到 P/Invoke - 但这将是更多的工作。

于 2009-04-05T14:52:28.620 回答
9

这可能被认为是一种 hack,但您可以考虑使用UWP StorageFolder API

C# 示例(尽管 F# 可能同样简单):

using Windows.Storage;

...

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

通过使用Lucian Wischik(Microsoft)的UWP for Desktop库,您可以轻松地从传统的 .NET 桌面和控制台应用程序中使用这些内容。

Install-Package UwpDesktop
于 2016-06-21T18:07:10.713 回答
5

实际上,根据for 的帮助Directory.GetFilesDirectory.EnumerateFiles会立即返回第一个结果(它是一个IEnumerable),而不是等待整个列表才返回。我相信这可能就是您正在寻找的。

于 2014-01-26T14:25:30.617 回答
3

我已经多次使用这种方法从函数/过程中获取 Async 对象,并且它总是很有效:


let AsyncGetDirectories path = 
    let fn = new Func<_, _>(System.IO.Directory.GetDirectories)
    Async.BuildPrimitive(path, fn.BeginInvoke, fn.EndInvoke)
于 2009-04-05T18:49:10.937 回答
0

Princess 的回答是在任务之间添加粒度的方法——所以这种事情会让其他玩家使用线程池:

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

当这些阻塞调用中的每一个都很重时,它并没有多大帮助 - 而 GetFiles over SMB 几乎就是的定义。

BeginRead我希望/EndRead目录有某种等价物,而GetFiles/GetDirectories只是对暴露一些异步变体的低级调用的一个很好的包装。像BeginReadDir/之类的东西EndReadDir

于 2009-04-05T16:46:49.220 回答
-1

我不是 F# 程序员,但我会在 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);
    }
}

我假设在 F# 中存在与迭代器类似的构造。

于 2013-01-01T21:24:33.180 回答