【问题标题】:Possible memory leak in simple batch file processing function in c#c# 中的简单批处理文件处理函数中可能存在内存泄漏
【发布时间】:2020-10-09 23:40:21
【问题描述】:

我正在运行一个非常简单的函数,它从文本文件中批量读取行。每行包含一个 sql 查询,因此该函数获取指定数量的查询,针对 SQL 数据库执行它们,然后获取下一批查询,直到读取整个文件。问题是随着时间的推移,文件非常大,该过程开始显着减慢。我猜函数中某处存在内存泄漏,但无法确定它可能在哪里。此功能运行时没有其他任何事情发生。我的编程技能充其量是粗略的,所以请放轻松。 :)

    for (int x = 0; x<= totalBatchesInt; x++)
    {
    var lines = System.IO.File.ReadLines(file).Skip(skipCount).Take(batchSize).ToArray();
    string test = string.Join("\n", lines);
    SqlCommand cmd = new SqlCommand(test.ToString());
        try
        {
            var rowsEffected = qm.ExecuteNonQuery(CommandType.Text, cmd.CommandText, 6000, true);
            totalRowsEffected = totalRowsEffected + rowsEffected;
            globalRecordCounter = globalRecordCounter + rowsEffected;
            fileRecordCounter = fileRecordCounter + rowsEffected;
            skipCount = skipCount + batchSize;
            TraceSource.TraceEvent(TraceEventType.Information, (int)ProcessEvents.Starting, "Rows 
            progress for " + folderName + "_" + fileName + " : " + fileRecordCounter.ToString() + " 
            of " + linesCount + " records");
        }
        catch (Exception esql)
        {           
            TraceSource.TraceEvent(TraceEventType.Information, (int)ProcessEvents.Cancelling, "Error 
            processing file " + folderName + "_" + fileName + " : " + esql.Message.ToString() + ". 
            Aborting file read");       
        }
    }

【问题讨论】:

  • sqlcommand implements iDisposable。但我怀疑这是你问题的核心。
  • 它可能不是核心,但现在你指出来,它肯定是一个问题。感谢您的意见。

标签: c# sql memory-management memory-leaks


【解决方案1】:

你的代码有很多问题:

  1. 你永远不会释放你的命令。这是 ODBC 驱动程序的本机句柄,等待垃圾收集器处理它是非常糟糕的做法。

  2. 无论如何,您都不应该单独发送这些命令。要么在一个命令中一次性发送它们,要么使用事务将它们组合在一起。

  3. 这就是它随时间变慢的原因:File.ReadLines(file).Skip(skipCount).Take(batchSize) 将一遍又一遍地读取同一个文件,并且每次尝试都会忽略越来越多的行,因此随着行数的增加越来越慢忽略(但已处理)变得越来越大。

要修复 #3,只需创建一次枚举器并分批迭代它。在纯 C# 中,您可以执行以下操作:

using var enumerator = File.ReadLines(file).GetEnumerator();

for (int x = 0; x<= totalBatchesInt; x++)
{
    var lines = new List<string>();
    while(enumerator.MoveNext() && lines.Count < batchSize)
        list.Add(enumerator.Current);
    string test = string.Join("\n", lines);
    // your code...
}

或者,如果您使用的是 Morelinq(我推荐),如下所示:

foreach(var lines in File.ReadLines(file).Batch(batchSize))
{
    // your code...
}

【讨论】:

  • 感谢您的耐心等待,这对#3 来说非常有意义。对于#2,分批发送它的原因是为了不使服务器过载,因为它正在接收其他并发请求。我通常一次发送一个包含 1000 行(sql 查询)的命令。或者,也许我误解了你在那里说的内容。 #1和#3现在看起来非常明显,因为您指出它们。我会尽快实施这些更改。非常感谢!!
  • 根据您的建议,我改用了 Morelinq,让我的生活变得轻松多了。性能大大提高。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-14
  • 2020-02-29
  • 1970-01-01
  • 2017-08-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多