【发布时间】:2016-01-03 17:48:00
【问题描述】:
我正在尝试实现一个通用文件系统爬虫,例如,它能够枚举从给定根目录开始的所有子文件夹。我想使用 async/await/Task 范例来做到这一点。
以下是我目前的代码。它有效,但我怀疑它可以改进。特别是,带注释的Task.WaitAll 会在深层目录树中导致不必要的等待,因为循环在每个树级别暂停等待,而不是立即着手处理添加到folderQueue 的新文件夹。
不知何故,我想将添加到folderQueue 的新文件夹包含在Task.WaitAll() 正在等待的任务列表中而WaitAll 正在进行中。这可能吗?
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
class FileSystemCrawlerSO
{
static void Main(string[] args)
{
FileSystemCrawlerSO crawler = new FileSystemCrawlerSO();
Stopwatch watch = new Stopwatch();
watch.Start();
crawler.CollectFolders(@"d:\www");
watch.Stop();
Console.WriteLine($"Collected {crawler.NumFolders:N0} folders in {watch.ElapsedMilliseconds} milliseconds.");
if (Debugger.IsAttached)
Console.ReadKey();
}
public int NumFolders { get; set; }
private readonly Queue<DirectoryInfo> folderQueue;
public FileSystemCrawlerSO()
{
folderQueue = new Queue<DirectoryInfo>();
}
public void CollectFolders(string path)
{
DirectoryInfo directoryInfo = new DirectoryInfo(path);
lock (folderQueue)
folderQueue.Enqueue(directoryInfo);
List<Task> tasks = new List<Task>();
do
{
tasks.Clear();
lock (folderQueue)
{
while (folderQueue.Any())
{
var folder = folderQueue.Dequeue();
Task task = Task.Run(() => CrawlFolder(folder));
tasks.Add(task);
}
}
if (tasks.Any())
{
Console.WriteLine($"Waiting for {tasks.Count} tasks...");
Task.WaitAll(tasks.ToArray()); //<== NOTE: THIS IS NOT OPTIMAL
}
} while (tasks.Any());
}
private void CrawlFolder(DirectoryInfo dir)
{
try
{
DirectoryInfo[] directoryInfos = dir.GetDirectories();
lock (folderQueue)
foreach (DirectoryInfo childInfo in directoryInfos)
folderQueue.Enqueue(childInfo);
// Do something with the current folder
// e.g. Console.WriteLine($"{dir.FullName}");
NumFolders++;
}
catch (Exception ex)
{
while (ex != null)
{
Console.WriteLine($"{ex.GetType()} {ex.Message}\n{ex.StackTrace}");
ex = ex.InnerException;
}
}
}
}
【问题讨论】:
-
我猜您正在尝试加快枚举文件夹的过程。您是否真的进行了测量以查看您获得了一些性能?请注意,没有真正的异步 API 可以枚举文件夹或文件(至少 AFAIK)。你所做的其实是使用更多线程来同步枚举文件夹,但是这些线程会花费大部分时间等待同步 IO 完成。
-
@YacoubMassad 是的,更快是这里的目标。实际上,我确实测量了我的解决方案与幼稚、顺序、同步枚举的性能,它肯定更快(尽管与直觉相反,在具有 12 个超核的机器上仅快约 3 倍;我期待更多改进)。我正在做的实际上是并行枚举文件夹 - 几个任务同时抓取不同文件夹的子文件夹。我相信 DirectoryInfo.GetDirectories 可以在不同的文件夹上并行执行(至少在 NTFS 中)。
-
你试过高性能吗?
标签: c# asynchronous filesystems async-await directory