2015-06-26 63 views
1

我有以下情形的輕微問題: 我給出的ID值的列表,我需要運行一個SELECT查詢(其中ID爲參數),然後將所有的結果集作爲一個大的結果集並將其返回給調用者。順序VS並行解決方案的內存使用情況

由於查詢可能每個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,但我注意到在任務管理器>>問題流程

+0

我認爲你應該結合串行和並行的方法,通過限制任務的數量,內核在你的系統的數量,每個任務的處理順序一些IDS等於IdsCount/CoresCount(調整與分工餘!)。 – Graffito

+0

我不認爲核心的限制會做任何事情;這些都是IO界限。這可能與Oracle驅動程序不會放棄內存有關。您不應該從列表中刪除任務或處理它們。 – Kit

+0

@Szeki:你是如何測量內存使用量的? – CharithJ

回答

0

使用實體框架的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數據的結果。

如果不清楚需要花費什麼時間,並且使用pre 10g oracle,那麼監控這個真的很難。但是,如果你使用實體框架,你可以附加監控! http://www.hibernatingrhinos.com/products/efprof

至少一年前Oracle支持實體框架5.

在順序執行它們一個接一個,並行他們從字面上得到在同一時間開始,所有的消費你的資源,創造死鎖。

+0

查詢本身甚至在PL/SQL Developer中運行幾分鐘。我可以使用EF並導入所有涉及的視圖,然後我可以在c#中重寫整個邏輯。但我更感興趣的是搞清楚爲什麼順序執行能夠正常工作,而帶任務的版本會導致內存使用量不斷增加。或者你認爲整個問題的根源是我檢索數據的方式? – Szeki

0

我不認爲你有在平行執行內存泄漏的任何證據。

可能是垃圾收集發生在不同的時間,這就是爲什麼有兩個不同的讀數。你不能指望它實時釋放內存。 .Net垃圾回收僅在需要時纔會發生。看看「Fundamentals of Garbage Collection

任務管理器或Process.GetCurrentProcess().PrivateMemorySize64可能不是很準確的方式來查找內存泄漏。如果這樣做,至少確保你叫完全的垃圾收集,並等待掛起終結之前讀內存計數器。

GC.Collect(); 
GC.WaitForPendingFinalizers(); 
相關問題