顺序与并行解决方案内存使用情况

本文关键字:用情 情况 内存 解决方案 并行 顺序 | 更新日期: 2023-09-27 17:59:47

我对以下场景有一个小问题:我得到了一个ID值列表,我需要运行一个SELECT查询(其中ID是一个参数),然后将所有结果集组合为一个大的结果集,并将其返回给调用者。

由于每个ID的查询可能会运行几分钟(这是另一个问题,但目前我认为这是一个给定的事实),并且输入中可能有1000个ID,所以我尝试使用任务。通过这种方法,我体验到了内存使用的缓慢但坚实的增加。

作为测试,我也做了一个简单的顺序解决方案,它有正常的内存使用图,但正如预期的那样,速度非常慢。它在运行时会有所增加,但当它完成时,一切都会回落到正常水平。

以下是代码的骨架:

public class RowItem
{
    public int ID { get; set; }
    public string Name { get; set; }
    //the rest of the properties
}

public List<RowItem> GetRowItems(List<int> customerIDs)
{
    // this solution has the memory leak
    var tasks = new List<Task<List<RowItem>>>();
    foreach (var customerID in customerIDs)
    {
        var task = Task.Factory.StartNew(() => return ProcessCustomerID(customerID));
        tasks.Add(task);
    }
    while (tasks.Any())
    {
        var index = Task.WaitAny(tasks.ToArray());
        var task = tasks[index];
        rowItems.AddRange(task.Result);
        tasks.RemoveAt(index);
    }
    // this works fine, but slow
    foreach (var customerID in customerIDs)
    {
        rowItems.AddRange(ProcessCustomerID(customerID)));
    }
    return rowItems;
}
private List<RowItem> ProcessCustomerID(int customerID)
{
    var rowItems = new List<RowItem>();
    using (var conn = new OracleConnection("XXX"))
    {
        conn.Open();
        var sql = "SELECT * FROM ...";
        using (var command = new OracleCommand(sql, conn))
        {
            using (var dataReader = command.ExecuteReader())
            {
                using (var dataTable = new DataTable())
                {
                    dataTable.Load(dataReader);
                    rowItems = dataTable
                               .Rows
                               .OfType<DataRow>()
                               .Select(
                                   row => new RowItem
                                   {
                                       ID = Convert.ToInt32(row["ID"]),
                                       Name = row["Name"].ToString(),
                                       //the rest of the properties
                                   })
                               .ToList();
                }
            }
        }
        conn.Close();
    }
    return rowItems;
}

使用任务时我做错了什么?根据MSDN的这篇文章,我不需要手动处理它们,但几乎没有其他东西。我想ProcessCustomerID是可以的,因为它在两种变体中都被调用了。

更新为了记录当前的内存使用情况,我使用了Process.GetCurrentProcess().PrivateMemorySize64,但我在任务管理器>>处理

顺序与并行解决方案内存使用情况

中注意到了这个问题

使用实体框架,ProcessCustomerID方法可能看起来像:

List<RowItem> rowItems;
using(var ctx = new OracleEntities()){
  rowItems = ctx.Customer
    .Where(o => o.id == customerID)
    .Select(
      new RowItem
      {
        ID = Convert.ToInt32(row["ID"]),
        Name = row["Name"].ToString(),
        //the rest of the properties
      }
    ).ToList();
}
return rowItems;

除非你正在传输大量数据,如图像、视频、数据或斑点,否则这应该是接近即时的,结果是1k数据。

如果不清楚什么需要时间,并且您使用10g之前的oracle,那么很难监控这一点。但是,如果您使用实体框架,您可以将监控附加到它上!http://www.hibernatingrhinos.com/products/efprof

至少一年前,Oracle支持实体框架5。

按照顺序,它们一个接一个地执行,并行地,它们实际上是在消耗资源和创建死锁的同时开始的。

我认为您没有任何证据表明并行执行中存在内存泄漏。

可能是垃圾收集发生在不同的时间,这就是为什么经历了两次不同的读数。你不能指望它能实时释放内存。只有在需要时才会进行网络垃圾收集。看看"垃圾收集基础"

任务管理器或Process.GetCurrentProcess().PrivateMemorySize64可能不是找到内存泄漏的非常准确的方法。如果这样做,至少要确保调用完全垃圾回收,并在读取内存计数器之前等待挂起的终结器。

GC.Collect();
GC.WaitForPendingFinalizers();