多个线程同时在同一文件夹上搜索

本文关键字:文件夹 搜索 线程 | 更新日期: 2023-09-27 17:57:23

目前我有一个大约 170,000 个 jpg 文件名的.txt文件,我将它们全部读入一个列表(文件名)。

我想搜索一个文件夹(此文件夹有子文件夹)以检查文件名中的每个文件是否存在于此文件夹中,如果存在,请将其复制到新文件夹中。

我做了一个粗略的估计,但每次搜索和复制文件名中的每个文件名大约需要 0.5 秒。所以 170,000 秒大约是 48 小时,所以除以 2,我的应用程序大约需要 24 小时才能使用 1 个线程搜索每个文件名! 显然这太长了,所以我想缩小范围并加快这个过程。使用多线程执行此操作的最佳方法是什么?

目前,我正在考虑制作 20 个单独的线程并将我的列表(文件名)拆分为 20 个不同的列表并同时搜索文件。例如,我将有 20 个不同的线程同时执行以下内容:

            foreach (string str in fileNames)
            {
                foreach (var file in Directory.GetFiles(folderToCheckForFileName, str, SearchOption.AllDirectories))
                {
                    string combinedPath = Path.Combine(newTargetDirectory, Path.GetFileName(file));
                    if (!File.Exists(combinedPath))
                    {
                        File.Copy(file, combinedPath);
                    }
                }
            }

更新以在下面显示我的解决方案:

            string[] folderToCheckForFileNames = Directory.GetFiles("C:''Users''Alex''Desktop''ok", "*.jpg", SearchOption.AllDirectories);
            foreach(string str in fileNames)
            {
                Parallel.ForEach(folderToCheckForFileNames, currentFile =>
                  {
                      string filename = Path.GetFileName(currentFile);
                      if (str == filename)
                      {
                          string combinedPath = Path.Combine(targetDir, filename);
                          if (!File.Exists(combinedPath))
                          {
                              File.Copy(currentFile, combinedPath);
                              Console.WriteLine("FOUND A MATCH AND COPIED" + currentFile);
                          }
                      }
                  }
                );
            }

感谢大家的贡献!非常感谢!

多个线程同时在同一文件夹上搜索

进行搜索时,不应使用普通的 foreach 语句,而应使用并行 linq。并行 linq 将 LINQ 语法的简单性和可读性与并行编程的强大功能相结合。就像面向任务并行库的代码一样。这将保护你免受低级线程操作和可能的异常(难以找到/调试的异常),同时在许多线程之间拆分你的工作。所以你可以做这样的事情:

fileNames.AsParallel().ForAll(str =>
            {
                var files = Directory.GetFiles(folderToCheckForFileName, str, SearchOption.AllDirectories);
                files.AsParallel().ForAll(file =>
                {
                    if (!string.IsNullOrEmpty(file))
                    {
                        string combinedPath = Path.Combine(newTargetDirectory, Path.GetFileName(file));
                        if (!File.Exists(combinedPath))
                        {
                            File.Copy(file, combinedPath);
                        }
                    }
                });
            });

>如果您的计算机少于 20 个内核,则 20 个不同的线程将无济于事。事实上,它会使过程变慢,因为您将 1) 必须花时间在每个线程之间切换上下文(这是您的 CPU 模拟超过 1 个线程/内核的方式)和 2) .NET 中的Thread为其堆栈保留 1 MB,这是相当大的。

相反,请尝试将 I/O 划分为async工作负载,对 CPU 密集型部分使用 Task.Run。此外,将您的Tasks数量保持在最多 4 到 8 个。

示例代码:

var tasks = new Task[8];
var names = fileNames.ToArray();
for (int i = 0; i < tasks.Length; i++)
{
    int index = i;
    tasks[i] = Task.Run(() =>
    {
        for (int current = index; current < names.Length; current += 8)
        {
            // execute the workload
            string str = names[current];
            foreach (var file in Directory.GetFiles(folderToCheckForFileName, str, SearchOption.AllDirectories))
            {
                string combinedPath = Path.Combine(newTargetDirectory, Path.GetFileName(file));
                if (!File.Exists(combinedPath))
                {
                    File.Copy(file, combinedPath);
                }
            }
        }
    });
}
Task.WaitAll(tasks);