如何在控制台应用程序中使用线程有效方式.net

时间:2015-11-18 16:03:17

标签: c# multithreading .net-4.0 task-parallel-library

我有一个8核心系统,我处理的文本文件数量包含数百万行说23个文件包含大量的行,需要2到3个小时才能完成。我正在考虑使用TPL任务处理文本文件。截至目前,我正在使用的代码是逐个顺序处理文本文件,所以我想将它分成5个文本文件在另一个线程中的一个线程5等等。这是一个好的方法还是其他任何方式?我使用的是.net 4.0,我正在使用的代码如下所示

foreach (DataRow dtr in ds.Tables["test"].Rows)
                {
                    string filename = dtr["ID"].ToString() + "_cfg";
                    try
                    {
                        foreach (var file in
                          Directory.EnumerateFiles(Path.GetDirectoryName(dtr["FILE_PATH"].ToString()), "*.txt"))
                        {
                            id = file.Split('\\').Last();
                            if (!id.Contains("GMML"))
                            {
                                strbsc = id.Split('_');
                                id = strbsc[0];
                            }
                            else
                            {
                                strbsc = file.Split('-');
                                id = ("RC" + strbsc[1]).Replace("SC", "");
                            }
                            ProcessFile(file, id, dtr["CODE"].ToString(), dtr["DOR_CODE"].ToString(), dtr["FILE_ID"].ToString());
                        }
                    }

如何将文本文件拆分为批处理,每个批处理应该在线程中逐个运行。如果23个文件,则在一个线程中为7,在一个线程中为7,在一个线程中为7,在另一个线程中为2。还有一件事是我将所有这些数据从文本文件移动到oracle数据库

修改

如果我这样使用它会值得,但如何将文件分成批次

Task.Factory.StartNew(() => {ProcessFile(file, id, dtr["CODE"].ToString(), dtr["DOR_CODE"].ToString(), dtr["FILE_ID"].ToString()); });

2 个答案:

答案 0 :(得分:3)

将文件拆分为多个块似乎不是一个好主意,因为它的性能提升与文件放置在磁盘上的方式有关。但是由于磁盘IO操作的异步特性,我强烈建议对该文件进行异步访问。有几种方法可以做到这一点,你总是可以选择这些方法的组合。 在最低级别,您可以使用异步方法(如StreamWriter.WriteAsync()或StreamReader.ReadAsync())访问磁盘上的文件,并合作让操作系统知道它可以切换到磁盘IO的新线程并让线程脱离直到磁盘IO操作完成。虽然在此级别进行异步调用很有用,但它本身并不会对应用程序的整体性能产生重大影响,因为您的应用程序仍在等待磁盘操作完成并且同时不执行任何操作! (这些调用会对您的软件从UI线程调用它们时的响应性产生重大影响) 因此,我建议将您的软件逻辑分成至少两个独立的部分,这些部分在两个独立的线程上运行;一个用于从文件中读取数据,另一个用于处理读取数据。您可以使用提供者/消费者模式来帮助这些线程进行交互。 .net提供的一个很棒的数据结构是System.Collections.Concurrent.ConcurrentQueue,它在实现多线程提供者/消费者模式时特别有用。

所以你可以轻松做到这样的事情:

System.Collections.Concurrent.ConcurrentQueue<string> queue = new System.Collections.Concurrent.ConcurrentQueue<string>();
bool readFinished = false;  
Task tRead = Task.Run(async () => 
{
    using (FileStream fs = new FileStream())
    {
        using (StreamReader re = new StreamReader(fs))
        {
            string line = "";
            while (!re.EndOfStream)
                queue.Enqueue(await re.ReadLineAsync());
        }
    }
});

Task tLogic = Task.Run(async () =>
{
    string data ="";
    while (!readFinished)
    {
        if (queue.TryDequeue(out data))
            //Process data
        else
            await Task.Delay(100);
    }
});

tRead.Wait();
readFinished = true;
tLogic.Wait();

这个简单的例子使用StreamReader.ReadLineAsync()从文件中读取数据,而一个好的做法是将一个固定长度的字符读入char []缓冲区并将该数据添加到队列中。您可以在一些测试后找到优化的缓冲区长度。

答案 1 :(得分:0)

所有,真正的瓶颈是当我进行大量插入时,我正在检查插入数据是否存在于数据库中或者是什么,我有一个状态列,如果数据存在,它将是'Y'或'N'通过做更新语句。因为插入的拥塞中的更新语句是罪魁祸首。在数据库中进行索引后,结果从4小时减少到10分钟,有什么影响,但它赢了:)