如何处理大量文件

时间:2016-12-10 22:30:46

标签: c# multithreading

我应该在目录和子目录中处理大约8000个文件。如何使用线程/任务轻松处理每个文件并等待所有文件?

目前,我使用此代码

var files = Directory.EnumerateFileSystemEntries(@"E:\Nouveau dossier (2)",
                                                 "*.*", SearchOption.AllDirectories);
foreach (var f in files)
{
    ToDo(f);
}

然而,它非常慢。

6 个答案:

答案 0 :(得分:3)

尝试使用Parallel.ForEach方法。但是,由于IO是bootleneck,它不会保证任何性能提升。

Parallel.ForEach(files, (f) => {
    ToDo(f);
});

system.threading.tasks.parallel.foreach

答案 1 :(得分:3)

请记住,任何正确答案都必须处理两件事。

  1. 读取文件 - 这可能并不能很好地并行工作,特别是在适当的磁盘(而不是SSD)上,因为它们必须将头部定位在每个文件上,并且显然不能并行完成。
  2. 处理文件 - 一旦数据在内存中,您就可以在不同的核心上处理它们。
  3. 现在,这两部分中哪一部分需要更多时间?如果它正在读取文件,并且通常情况就是这样,那么使用多个内核就没有任何帮助。他们仍然需要等待数据进入。

    我给你的建议是做一个实验。不处理文件,只读它们。使您的ToDo()函数只需将文件读取到最后。这是整个工作的最短时间。

    然后尝试并行读取文件,但要准备好看到它需要比以前更多的时间......你可以这样做:

    Parallel.ForEach(files, ReadToEnd);
    

    这假设ReadToEnd()是您的测试函数,它只读取文件的内容。

答案 2 :(得分:1)

您可以使用Parallel课程。

请在下面找到一个例子:

class Program
{

    static void Main(string[] args)
    {
    var files = Directory.EnumerateFileSystemEntries(@"C:\Users\Myleo\Pictures", "*.*", SearchOption.AllDirectories);
    var program = new Program();
    var result = program.ProcessInParallelWithCounter(files);
    Console.WriteLine("count: {0}", result);

    #if DEBUG
        Console.ReadKey();
    #endif
}

private void ProcessInParallel(IEnumerable<string> files)
{
    // process
    Parallel.ForEach(files, Process);
}

private int ProcessInParallelWithCounter(IEnumerable<string> files)
{
    // process and count
    var counter = 0;
    Parallel.ForEach(
        files,
        () => 0,
        (file, loopState, localCount) =>
                                        {
                                            Process(file);
                                            return ++localCount;
                                        },
        count => Interlocked.Add(ref counter, count));
    return counter;
}

private void Process(string file)
{
    // your code.
}

}

答案 3 :(得分:1)

刚刚关闭,这里有几种方法可以使用多个线程来更好地利用CPU功率来加速处理,假设这是瓶颈所在。例如,如果瓶颈是磁盘I / O,则可能无法提高性能。

  1. ThreadPool.QueueUserWorkItem Method
  2. Task Parallel Library (TPL) ForEach
  3. TaskFactory.StartNew Method

答案 4 :(得分:0)

因为读取文件是IO操作,所以async/await方法似乎是此任务的最佳方法。

在IO读取或写入文件时,您不需要浪费线程等待。读取或写入文件的操作包含&#34; waiting&#34;用于IO设备的响应。创建无效的单独线程 - &gt;只有等待是浪费资源,没有为您的应用程序提供任何价值。

通过使用async/await,您可以只使用一个线程执行相同的工作。当第一个任务等待文件被读取时,将启动另一个任务,依此类推。

您可以让ToDo方法异步工作

public async Task ToDoAsync(string file)
{
    using (var fileReader = File.OpenText(file))
    {
        var allFile = await fileReader.ReadToEndAsync();
        // and do something
    }
}

然后使用它

var files = Directory.EnumerateFileSystemEntries(@"E:\Nouveau dossier (2)", "*.*", SearchOption.AllDirectories);

var tasks = new List<Task>();
foreach (var f in files)
{
    var task = ToDoAsync(f);
    tasks.Add(task);
} 

await Task.WhenAll(tasks.ToArray());

因此,为了获得更好的性能和更好的资源使用,您需要将逻辑划分为两部分,如@Zoran答案所述。

  • 读取可以异步方式完成的文件
  • 处理可以在&#34; Parallel&#34;
  • 中完成的数据

答案 5 :(得分:0)

您可以在单独的任务中运行ToDo

var files = Directory.EnumerateFileSystemEntries(@"E:\Nouveau dossier (2)", "*.*", SearchOption.AllDirectories);
List<Task> tasks = new List<Task>();
foreach (var f in files)
{
    var local = f;
    var tast = Task.Run(() => ToDo(local));

    tasks.Add(task);
}

Task.WhenAll(tasks.ToArray());