Вызов метода после завершения 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
закончил свою работу?
Примечание. Как вы, возможно, знаете, я не знаю, сколько потоков будет создано, потому что я не знаю, сколько там файлов. И установка тайм-аута не вариант.
2 ответа
Использование QueueUserWorkItem() - это простой подход. Без контроля над вашей работой, это огонь и забыть.
Task
s запускаются поверх 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() в идеале должна использовать асинхронный ввод / вывод, но в противном случае вы можете 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
содержит операции ввода-вывода, которые я бы рассмотрел для обработки await
как предложил Хенк Холтерман как Parallel.ForEach
не зависит от нагрузки ввода / вывода.