【发布时间】:2015-06-26 09:56:00
【问题描述】:
我对以下场景有一个小问题: 我得到了一个 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;
}
使用任务时我做错了什么?根据this MSDN article,我不需要手动处理它们,但几乎没有其他东西。我猜 ProcessCustomerID 没问题,因为它在两种变体中都被调用。
更新
为了记录当前的内存使用情况,我使用了Process.GetCurrentProcess().PrivateMemorySize64,但我在任务管理器>>进程中注意到了问题
【问题讨论】:
-
我认为你应该结合顺序和并行的方法,通过将任务的数量限制为系统中的核心数量,每个任务顺序处理多个等于 IdsCount / CoresCount 的 Id(调整除法余数!)。
-
我认为核心限制没有任何作用;这些是 IO 绑定的。这可能与 Oracle 驱动程序没有放弃内存有关。您不必从列表中删除或处置它们。
-
@Szeki:你是如何测量内存使用情况的?
标签: c# .net oracle memory-leaks task