我正在使用用C#编写的控制台应用程序
该应用程序的目的是浏览所有驱动器和文件,并对它们进行处理。但是用一个线程浏览所有文件是一个耗时的过程,这不是我的目标。
所以我决定使用ThreadPool
来处理它:
class Program () {
static void Main(string[] args) {
foreach (var d in DriveInfo.GetDrives()) {
ThreadPool.QueueUserWorkItem(x => Search(d.RootDirectory.GetDirectories()));
}
Console.WriteLine("Job is done.");
Console.ReadKey();
}
private static void Search(DirectoryInfo[] dirs) {
foreach (var dir in dirs) {
try {
foreach (var f in dir.GetFiles()) {
ThreadPool.QueueUserWorkItem(x => DoTheJob(f));
}
ThreadPool.QueueUserWorkItem(x => Search(dir.GetDirectories()));
} catch (Exception ex) {
continue;
}
}
}
}
问题是Console.WriteLine("Job is done.")
在所有线程完成之前执行。我已经阅读了一些问题和答案,但是都没有解决我的问题。
ThreadPool
中的所有线程完成工作后,如何调用方法?
注意:您可能知道,我不知道将创建多少个线程,因为我不知道有多少个文件。而且设置超时不是一种选择。
答案 0 :(得分:1)
使用QueueUserWorkItem()是底层的准系统方法。无法掌控您的工作,这是生死攸关的事情。
Task
在ThreadPool之上运行,async/await
可以在这里解决您的问题。
顶层:
var tasks = new List<Task>();
foreach (var d in DriveInfo.GetDrives())
{
tasks.Add( Search(d.RootDirectory.GetDirectories()));
}
Task.WaitAll(tasks.ToArray());
然后您的Search()变为
private static async Task Search(DirectoryInfo[] dirs)
{
...
foreach(...)
{
await Task.Run(...);
}
await Search(dir.GetDirectories());
}
理想情况下,DoTheJob()应该使用异步I / O,否则您可以await Task.Run( () => DoTheJob(f))
答案 1 :(得分:0)
以下是如何使用Parallel.ForEach
产生公平负载的示例:
static IEnumerable<FileSystemInfo> GetFileSystemObjects(DirectoryInfo dirInfo)
{
foreach (var file in dirInfo.GetFiles())
yield return file;
foreach (var dir in dirInfo.GetDirectories())
{
foreach (var fso in GetFileSystemObjects(dir))
yield return fso;
yield return dir;
}
}
static void Main(string[] args)
{
var files = GetFileSystemObjects(new DirectoryInfo(<some path>)).OfType<FileInfo>();
Parallel.ForEach(files, f =>
{
DoTheJob(f);
});
}
如果DoTheJob
包含受I / O约束的操作,我会考虑使用await
来处理它,因为 Henk Holterman 建议Parallel.ForEach
与I / O负载。