目前我有一个大约170,000个jpg文件名的.txt文件,我将它们全部读入List(fileNames)。
我想搜索一个文件夹(此文件夹有子文件夹)来检查fileNames中的每个文件是否都存在于此文件夹中,如果存在,请将其复制到新文件夹。
我正在粗略估计,但fileNames中每个文件名的每次搜索和复制大约需要0.5秒。所以170,000秒大约是48小时,所以除以2需要大约24小时,我的应用程序使用1个线程搜索每个文件名!显然这太长了所以我想缩小范围并加快进程。使用多线程进行此操作的最佳方法是什么?
目前我正在考虑制作20个单独的线程并将我的列表(fileNames)拆分为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);
}
}
}
);
}
感谢大家的贡献!非常赞赏!
答案 0 :(得分:0)
Thread
保留1 MB的堆栈,非常大。
相反,尝试将I / O划分为async
工作负载,使用Task.Run
作为CPU绑定/密集部分。另外,最多可将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);
答案 1 :(得分:0)
在进行搜索时,不应使用普通的foreach语句,而应使用并行linq。 Parallel 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);
}
}
});
});