如何使用c#将文本文件并行拆分为多个文件

时间:2014-12-04 03:47:14

标签: c# multithreading parallel-processing parallel.foreach

我一直在研究,我发现了Parallel.For的内容,但我无法弄清楚如何编码而没有出现任何错误。

我不断得到的一个错误是,有多个处理器试图访问同一个文件。

我目前的代码是顺序的,但需要很长时间。我的文本文件是10GB。

这是我的顺序部分,我没有尝试将其并行

for (int i = 0; i <= 10; i++)
            {
               Console.WriteLine("Parsing List: " + i);
               min_chunk += chunk;
                max_chunk += chunk;
                if (max_chunk >= lines)
                {
                    max_chunk = lines - 1;
                }
                if (i == 0)
                {
                    min_chunk = 0;
                    max_chunk = chunk;
                }
                int diff = (int)(max_chunk - min_chunk);
                splitFile("sort.txt", min_chunk, max_chunk, i);
            }
public static void splitFile(string path, int min, int max, int threadnum)
        {

            string outFileName = String.Concat("list", threadnum, ".txt");
            System.IO.StreamWriter outfile = new System.IO.StreamWriter(outFileName);


            for (int currline = min; currline < max; currline++)
            {
                string line = File.ReadLines("sort.txt").Skip(currline).Take(1).First();
                outfile.WriteLine(line);
            }

            outfile.Close();
        }
    }

2 个答案:

答案 0 :(得分:2)

以下是与您的问题相关的几个已经回答的链接

答案 1 :(得分:1)

您不需要多个线程来加快速度。

你真正想要的是读取文件一次,并在你去的时候拆分它。我并不真正了解您使用min_chunkmax_chunk做了什么,但我建议您定义一个块大小,比如它的10000行。然后你可以这样做:

int maxLines = 10,000;
int numLines = 0;
int fileNumber = 0;
var writer = File.CreateText("list" + fileNumber + ".txt");
foreach (var line in File.ReadLines("sort.txt"))
{
    writer.WriteLine(line);
    ++numLines;
    if (numLines == maxLines)
    {
        writer.Close();
        numLines = 0;
        ++fileNumber;
        writer = File.Create("list" + fileNumber + ".txt");
    }
}
writer.Close();

使用多个线程拆分单个文本文件通常不会加快速度。有两个原因。

首先,如果你有10个线程,第一个线程读取前N行并输出它们。同时,第二个线程正在读取相同的文件,跳过前N行并写下接下来的N行。使用10个线程,您可以将文件打开10次,除了其中一个线程之外的所有线程都花费大部分时间来阅读和跳过它不关心的内容。

此外,磁盘一次只能做一件事。当多个线程尝试写入单个磁盘时,它比单个线程更慢 。当一个线程写入磁盘时,它只能写...并写入...并写入。当多个线程试图写入时,一个写入,然后磁盘必须重新定位读/写头,然后才能为下一个线程写入等。这些重新定位(称为头部搜索)需要花费大量时间 - 在命令上5到10毫秒,这是CPU时间的永恒。发生的事情是你的线程大部分时间都在等待其他线程写入。

更新

如果出于某种原因,您已经决定使用多个线程执行此操作,则需要在splitFile方法中修复此循环:

        for (int currline = min; currline < max; currline++)
        {
            string line = File.ReadLines("sort.txt").Skip(currline).Take(1).First();
            outfile.WriteLine(line);
        }

鉴于该循环以及min = 100max = 200,它将会读取该文件100次。它将第一次跳过100行并输出1.然后它将关闭文件,下一次循环它将跳过101行并输出1.这将花费相当多的时间。很长一段时间。

您可以将其更改为:

foreach (var line in File.ReadLines("sort.txt").Skip(min).Take(max-min))
{
    outfile.WriteLine(line);
}

事实上,如果你真的想要看中,你可以写:

File.WriteAllLines(outFileName, File.ReadLines("sort.txt").Skip(min).Take(max-min));

但是你仍然有多个线程试图访问相同的输入文件的问题。如果File.ReadLines以独占模式打开文件,那么您有两个选择:

  1. 使用锁来防止多个文件同时尝试访问该文件
  2. 使用许可共享打开文件
  3. 选项2的一个例子:

    using (var fs = new FileStream("sort.txt", FileMode.Open, FileAccess.Read, FileShare.Read))
    {
        using (var reader = new StreamReader(fs))
        {
            int i = 0;
            while (!reader.EndOfStream && i < max)
            {
                string line = reader.ReadLine();
                if (i > min)
                    outfile.WriteLine(line);
                ++i;
            }
        }
    }
    

    这会做你要求的。但是,这并不是一种非常聪明的做事方式,因为你有10个线程同时读取同一个文件,而且大多数都是花时间跳过线路。你做了很多不必要的工作。我首先介绍的简单单线程版本将胜过这一点,特别是如果输出文件都在同一个驱动器上。