读大文本文件多线程?

时间:2013-06-19 09:59:44

标签: c#

我有100000行的大型txt文件。 我需要启动n-count个线程并从该文件中为每个线程提供uniq行。 做这个的最好方式是什么?我想我需要逐行读取文件,迭代器必须是全局的才能锁定它。将txt文件加载到列表将非常耗时,我可以收到OutofMemory异常。有任何想法吗?用一些代码帮助plz。

6 个答案:

答案 0 :(得分:37)

您可以使用File.ReadLines Method逐行读取文件,而无需一次将整个文件加载到内存中,Parallel.ForEach Method可以并行处理多个线程中的行:

Parallel.ForEach(File.ReadLines("file.txt"), (line, _, lineNumber) =>
{
    // your code here
});

答案 1 :(得分:15)

执行我自己的基准测试,将61,277,203行加载到内存中并将值推送到Dictionary / ConcurrentDictionary()后,结果似乎支持@dtb上面的答案,使用以下方法是最快的:

Parallel.ForEach(File.ReadLines(catalogPath), line =>
{

}); 

我的测试还显示以下内容:

  1. File.ReadAllLines()和File.ReadAllLines()。AsParallel()似乎在此大小的文件上以几乎完全相同的速度运行。看看我的CPU活动,看起来它们似乎都使用了我的8个内核中的两个?
  2. 首先使用File.ReadAllLines()读取所有数据似乎比在Parallel.ForEach()循环中使用File.ReadLines()要慢得多。
  3. 我还尝试了一个生产者/消费者或MapReduce样式模式,其中一个线程用于读取数据,另一个线程用于处理它。这似乎也没有超越上面的简单模式。
  4. 我已提供此模式的示例以供参考,因为它未包含在此页面中:

    var inputLines = new BlockingCollection<string>();
    ConcurrentDictionary<int, int> catalog = new ConcurrentDictionary<int, int>();
    
    var readLines = Task.Factory.StartNew(() =>
    {
        foreach (var line in File.ReadLines(catalogPath)) 
            inputLines.Add(line);
    
            inputLines.CompleteAdding(); 
    });
    
    var processLines = Task.Factory.StartNew(() =>
    {
        Parallel.ForEach(inputLines.GetConsumingEnumerable(), line =>
        {
            string[] lineFields = line.Split('\t');
            int genomicId = int.Parse(lineFields[3]);
            int taxId = int.Parse(lineFields[0]);
            catalog.TryAdd(genomicId, taxId);   
        });
    });
    
    Task.WaitAll(readLines, processLines);
    

    以下是我的基准测试:

    enter image description here

    我怀疑在某些处理条件下,生产者/消费者模式可能胜过简单的Parallel.ForEach(File.ReadLines())模式。但是,在这种情况下并没有。

答案 2 :(得分:7)

在一个线程上读取文件,将其行添加到blocking queue。启动从该队列中读取的N个任务。设置队列的max size以防止内存不足错误。

答案 3 :(得分:4)

类似的东西:

public class ParallelReadExample
{
    public static IEnumerable LineGenerator(StreamReader sr)
    {
        while ((line = sr.ReadLine()) != null)
        {
            yield return line;
        }
    }

    static void Main()
    {
        // Display powers of 2 up to the exponent 8:
        StreamReader sr = new StreamReader("yourfile.txt")

        Parallel.ForEach(LineGenerator(sr), currentLine =>
            {
                // Do your thing with currentLine here...
            } //close lambda expression
        );

        sr.Close();
    }
}

认为它会起作用。 (这里没有C#编译器/ IDE)

答案 4 :(得分:2)

如果要将线程数限制为n,最简单的方法是使用AsParallel()WithDegreeOfParallelism(n)来限制线程数:

string filename = "C:\\TEST\\TEST.DATA";
int n = 5;

foreach (var line in File.ReadLines(filename).AsParallel().WithDegreeOfParallelism(n))
{
    // Process line.
}

答案 5 :(得分:2)

正如上面提到的@dtb,读取文件然后处理文件中各行的最快方法是: 1)将File.ReadAllLines()放入数组中 2)使用Parallel.For循环迭代数组。

You can read more performance benchmarks here.

您必须编写的代码的基本要点是:

string[] AllLines = File.ReadAllLines(fileName);
Parallel.For(0, AllLines.Length, x =>
{
    DoStuff(AllLines[x]);
    //whatever you need to do
});

随着.Net4中更大的阵列大小的引入,只要你有足够的内存,这应该不是问题。