dotNet中是否存在DirectoryInfo.GetFiles / Directory.GetDirectories的异步版本?我想在F#异步块中使用它们,并且拥有一个可以使用AsyncCallbacks调用的版本会很好。
问题是我试图吮吸一堆目录,可能是在慢速网络连接上的SMB挂载上,我不希望一堆线程池线程在等待网络读取时可能正在做其他工作
答案 0 :(得分:13)
我没有找到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) }
答案 1 :(得分:11)
不,我认为没有。池线程方法可能是最实用的。或者,我猜你可以下载到P / Invoke - 但这将是很多更多的工作。
答案 2 :(得分:5)
这可能被认为是一种黑客行为,但您可以考虑使用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
答案 3 :(得分:3)
我已经多次使用这种方法从函数/过程中获取Async对象,并且它总是很好用:
let AsyncGetDirectories path =
let fn = new Func<_, _>(System.IO.Directory.GetDirectories)
Async.BuildPrimitive(path, fn.BeginInvoke, fn.EndInvoke)
答案 4 :(得分:3)
实际上,根据help for Directory.GetFiles
,Directory.EnumerateFiles
将立即返回第一个结果(它是IEnumerable
),而不是在返回之前等待整个列表。我相信这可能就是你要找的东西。
答案 5 :(得分:1)
可能也会看到
http://weblogs.asp.net/podwysocki/archive/2009/03/18/functional-net-laziness-becomes-you.aspx
答案 6 :(得分: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");
当这些阻塞调用中的每一个都很重时,它没有那么多 - 而且基于SMB的GetFiles几乎就是 heavy 的定义。
我希望目录有BeginRead
/ EndRead
的某种等价物,GetFiles
/ GetDirectories
只是低级别调用的一个很好的包装器暴露了一些异步变种。类似于BeginReadDir
/ EndReadDir
。
答案 7 :(得分:0)
我不是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#中的迭代器有类似的构造。