使用C#中的线程将大文本文件(500万条记录)并行拆分为较小的文件

时间:2016-06-22 17:10:18

标签: c# multithreading parallel-processing task-parallel-library .net-framework-version

我有一个包含500万条记录(5列和500万行)的大型文本文件。文件图像如下所示

Large text file to be split

对于拆分,我使用了线程的概念。我创建了10个线程来分割更大的文件。我在读取较大的文件时使用了字符串数组来存储值。代码如下所示。

class Program
{
    const string sourceFileName = @"C:\Users\Public\TestFolder\ThreadingExp\NewMarketData.txt";
    const string destinationFileName = @"C:\Users\Public\TestFolder\ThreadingExp\NewMarketData-Part-{0}.txt";

    static void Main(string[] args)
    {
        int[] index = new int[20];
        index[0] = 0;
        for(int i=1;i<11;i++)
        {
            index[i] = index[i-1]+500000;
        }

        //Reading Part
        var sourceFile = new StreamReader(sourceFileName);
        string[] ListLines = new string[5000000];
        for (int i = 0; i < 5000000; i++)
        {
            ListLines[i] = sourceFile.ReadLine();
        }            

        //Creating array of threads
        Thread[] ArrayofThreads = new Thread[10];
        for (int i = 0; i < ArrayofThreads.Length; i++)
        {
            ArrayofThreads[i] = new Thread(() => Writing(ListLines,index[i], index[i+1]));
            ArrayofThreads[i].Start();
        }

        for (int i = 0; i < ArrayofThreads.Length; i++)
        {
            ArrayofThreads[i].Join();
        }
    }
    static void Writing(string[] array, int a, int b)
    {
        //Getting the thread number
        int id= Thread.CurrentThread.ManagedThreadId;

        var destinationFile = new StreamWriter(string.Format(destinationFileName,id));

        string line;
        for (int i = a; i< b;i++ )
        {
            line = array[i];
            destinationFile.WriteLine(line);
        }

        destinationFile.Close();         
    }

}   

代码工作正常。这里并行写入不同的文件。但是对于阅读,我已经将整个内容存储在一个数组中,然后通过不同的线程传递,以便使用索引进行写入。我想使用线程并行执行这两项任务(读取较大的文件并写入不同的小文件)。

1 个答案:

答案 0 :(得分:3)

使用单个帖子,你几乎肯定会做得更好。

首先,必须顺序读取文本文件。没有捷径可以让你跳过去找到第500,000行,而不先读取它前面的499,999行。

其次,即使您可以这样做,磁盘驱动器一次只能处理一个请求。它不能同时从两个地方读书。因此,当您正在阅读文件的一部分时,想要读取文件另一部分的线程只是坐在那里等待磁盘驱动器。

最后,除非你的输出文件在不同的驱动器上,否则你会遇到与读取相同的问题:磁盘驱动器一次只能做一件事。

所以你最好从简单的事情开始:

const int maxLinesPerFile = 5000000;
int fileNumber = 0;
var destinationFile = File.CreateText("outputFile"+fileNumber);

int linesRead = 0;
foreach (var line in File.ReadLines(inputFile))
{
    ++linesRead;
    if (linesRead > maxLinesPerFile)
    {
        destinationFile.Close();
        ++fileNumber;
        destinationFile = File.CreateText("outputFile"+fileNumber);
    }
    destinationFile.WriteLine(line);
}
destinationFile.Close();

如果输入和输出文件位于不同的驱动器上,则可以通过两个线程节省一点时间:一个用于输入,一个用于输出。他们会使用BlockingCollection进行通信。基本上,输入线程会将行放入队列,输出线程将从队列中读取并输出文件。从理论上讲,读取时间与写入时间重叠,但事实是队列填满了,读者最终不得不等待写作线程。你的某些性能会提高,但不会达到你所期望的水平。