1

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

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

4

2 回答 2

1

使用 QueueUserWorkItem() 是低级的准系统方法。如果无法控制您的工作,那就是一劳永逸。

Tasks 运行在 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))

于 2019-05-08T15:55:02.563 回答
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);
    });
}

但是,如果包含 I/O 绑定操作,我会考虑按照Henk Holterman的建议DoTheJob处理它,因为它与I/O 负载无关。awaitParallel.ForEach

于 2019-05-08T18:30:01.797 回答