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

C# 多个线程同时在同一文件夹上搜索,c#,multithreading,file-search,C#,Multithreading,File Search,目前我有一个大约170000个jpg文件名的.txt文件,我把它们都读入一个列表(文件名) 我要搜索一个文件夹(此文件夹有子文件夹),以检查文件名中的每个文件是否存在于此文件夹中,如果存在,请将其复制到新文件夹 我做了一个粗略的估计,但每次搜索和复制文件名中的每个文件名大约需要0.5秒。170000秒大约是48小时,所以除以2,我的应用程序需要24小时才能用1个线程搜索到每个文件名!显然这太长了,所以我想缩小范围,加快进程。使用多线程执行此操作的最佳方法是什么 目前,我正在考虑制作20个独立的线

目前我有一个大约170000个jpg文件名的.txt文件,我把它们都读入一个列表(文件名)

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

我做了一个粗略的估计,但每次搜索和复制文件名中的每个文件名大约需要0.5秒。170000秒大约是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);
                          }
                      }

                  }
                );

            }

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

20个不同的线程如果您的计算机的内核少于20个,则没有任何帮助。事实上,它会使进程变慢,因为1)您将不得不花费时间在每个线程之间切换上下文(这是您的CPU模拟多个线程/核心的方式),2).NET中的
线程为其堆栈保留1MB,这相当大

相反,尝试使用
Task将I/O划分为
async
工作负载。为CPU限制/密集型部分运行
。另外,将
任务的数量保持在最多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);
var tasks=新任务[8];
var names=fileNames.ToArray();
for(int i=0;i
{
for(int current=index;current
如果您的计算机的内核少于20个,那么20个不同的线程将没有帮助。事实上,它会使进程变慢,因为1)您将不得不花费时间在每个线程之间切换上下文(这是您的CPU模拟多个线程/核心的方式),2).NET中的
线程为其堆栈保留1MB,这相当大

相反,尝试使用
Task将I/O划分为
async
工作负载。为CPU限制/密集型部分运行
。另外,将
任务的数量保持在最多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);
var tasks=新任务[8];
var names=fileNames.ToArray();
for(int i=0;i
{
for(int current=index;current
在执行搜索时,应该使用并行linq,而不是使用普通的foreach语句。将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);
                        }
                    }
                });
            });

在进行搜索时,应该使用并行linq,而不是使用普通的foreach语句。将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);
                        }
                    }
                });
            });

如果我没看错的话,为什么不把所有的文件名像哈希集一样读入内存,然后用它来搜索文件呢。至于使用多线程加快磁盘IO速度,这仅限于此。一旦磁盘IO达到最大值,不管你有多少线程。不仅仅是磁盘IO,它还严重依赖于可用于处理线程逻辑的处理内核的数量,因此最终是一个糟糕的解决方案。你尝试过使用TPL foreach吗?你们是说要把所有实际的.jpg文件从folderToCheckForFileName读入内存并搜索?与其检查我机器上的实际文件夹,不如不读取文件,只读取文件名列表。如果我读对了,为什么不将所有文件名一次性读取到内存中(如哈希集),然后使用它搜索文件。至于使用多线程加快磁盘IO速度,这仅限于此。一旦磁盘IO达到最大值,不管你有多少线程。不仅仅是磁盘IO,它还严重依赖于可用于处理线程逻辑的处理内核的数量,因此最终是一个糟糕的解决方案。你尝试过使用TPL foreach吗?你们是说要把所有实际的.jpg文件从folderToCheckForFileName读入内存并搜索?而不是检查实际的fol