【问题标题】:How to use threading effective way in a console application .net如何在控制台应用程序.net中使用线程有效的方式
【发布时间】:2015-11-18 16:03:16
【问题描述】:

我有 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()); });

【问题讨论】:

  • 你的代码瓶颈在哪里?如果它“运行缓慢”,您需要衡量这种缓慢来自何处。是因为CPU使用率,还是因为IO?如果是后者,多线程不会帮助您,并且(很可能)会使事情变慢。你需要确定。
  • 您的单线程方法是否最大限度地利用了单核?如果不是,那么 IO 很可能是瓶颈,而在多个线程上执行相同操作的简单方法不太可能产生您想要的性能提升。
  • @peter 您没有提供足够的信息来启用回复。正如 spender 所问的那样,您需要在做出不知情的更改之前确定瓶颈在哪里。您没有指定 ProcessFile 的作用,但如果它是一段效率低下的代码,那么 Threading 会使情况变得更糟。如果 ProcessFile 正在执行读取和写入操作,并且已经破坏了磁盘,那么您将使其变得更糟。您需要在更改之前确定瓶颈所在。
  • @spender 我回答了我的问题

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


【解决方案1】:

将文件分成多个块似乎不是一个好主意,因为它的性能提升与文件在磁盘上的放置方式有关。但由于磁盘 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[] 缓冲区中并将该数据添加到队列中。经过一些测试,您可以找到优化的缓冲区长度。

【讨论】:

    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 2017-04-10
      • 1970-01-01
      • 1970-01-01
      • 2019-05-18
      • 2020-02-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多