ThreadPool.QueueUserWorkItem完成后调用方法

时间:2019-05-08 15:40:22

标签: c# multithreading threadpool queueuserworkitem file-traversal

我正在使用用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中的所有线程完成工作后,如何调用方法?

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

2 个答案:

答案 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负载。