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



我一直在研究,我发现了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();
        }
    }

这里有一些已经回答的与您的问题相关的链接

  • 拆分大文件
  • 并行 C#
  • 将文本文件拆分为多个

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

您真正想要的是读取文件一次,然后随时拆分。我真的不明白你在用min_chunkmax_chunk做什么,但我建议你定义一个块大小,比如说它是 10,000 行。然后,您可以执行以下操作:

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. 使用宽松共享打开文件

选项 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 个线程同时读取同一个文件,并且其中大多数线程都花时间跳过行。你正在做很多不必要的工作。我首先介绍的简单单线程版本将优于此版本,特别是如果输出文件都在同一驱动器上。

最新更新