在 ThreadPool.QueueUserWorkItem 完成后调用方法



我正在开发一个用 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中的所有线程完成其工作后调用方法?

注意:您可能知道,我不知道将创建多少个线程,因为我不知道有多少个文件。并且设置超时不是一种选择。

使用 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());

然后你搜索()变成

private static async Task Search(DirectoryInfo[] dirs)
{
... 
foreach(...)
{
await Task.Run(...);
} 
await Search(dir.GetDirectories());
}

DoTheJob() 的东西应该理想地使用异步 I/O,否则你可以await Task.Run( () => DoTheJob(f))

下面是如何使用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 绑定操作,我会考虑按照Henk Holterman的建议await处理它,因为Parallel.ForEach与 I/O 负载无关。

相关内容

  • 没有找到相关文章

最新更新