2010-12-13 83 views
1

我一直在下面的代碼中發現內存不足異常,我想知道是否有什麼我可以做,以阻止這種情況發生。線程池內存異常

private static List<string> MyIds { get; set; } 
    private static object LockObject { get; set; } 
    private static int Counter { get; set; } 
    private static readonly NumOfThreads = 5; 

    static void Main(string[] args) 
    { 
     try 
     { 
      Console.Clear(); 
      LockObject = new object(); 
      // Pull id's into memory (A list of around 1 million ids) 
      MyIds = _repository.GetIds(); 
      for (int i = 0; i < NumOfThreads ; i++) 
       ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), (object)i); 

     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex.StackTrace); 
     } 
    } 

    public static void DoWork(Object stateInfo) 
    { 
     while (MyList.Count > 0) 
     { 
      lock (LockObject) 
      { 
       if (MyList.Count == 0) 
       return; 

      string id = MyList[0]; 

      var record = _repository.GetRecord(id); 
      _repository.Add(record); 

      Counter++; 
      if (Counter % 100 == 0) 
          System.Console.WriteLine(DateTime.Now + " - Imported " + Counter.ToString() + " Records.."); 

       MyList.RemoveAt(0); 
      } 
     } 
    } 

感謝所有幫助

+3

很少有觀察結果與OOM無關。 1.您正在序列化訪問DoWork中的所有代碼,因此使用多個線程沒有意義。 2.您沒有使用傳入的stateInfo值。 3.您在鎖外訪問MyList,以便此訪問不是線程安全的。 – 2010-12-13 21:24:25

回答

5

您是從列表的開始,這將導致生成一個新的列表和舊複製到它刪除。考慮到您正在處理包含大量元素的列表,這將對Large Object Heap造成嚴重破壞。

如果您必須使用這種類型的設計以相反的方向移除項目,這將防止複製列表的底層數組,即從末端開始移除。

更好的設計是使用計數器,使用Interlocked.Increment遞增並使用它訪問列表中的成員。您可以安全地做到這一點,因爲您在創建完列表後沒有更改清單。

更新

從我的評論

您序列化訪問所有代碼的DoWork所以有在使用多線程是沒有意義的。

類似以下內容將避免從您的ID列表中刪除問題,再加上允許您可能從您的存儲庫中同時檢索項目,因此利用這些額外的線程。這將不得不測量 - 添加線程不是性能改進的保證。

另外,如果您的「_repository」是一個集合,請確保將其大小設置爲與您的ID列表大小相同的大小。這會防止大量的中間數組複製,因爲集合隨着添加項目而增長。

private static int _counter = -1; 

    public static void DoWork(Object stateInfo) 
    { 
     int index; 

     while ((index = Interlocked.Increment(ref _counter)) < MyList.Count) 
     { 
      string id = MyList[index]; 

      var record = _repository.GetRecord(id); 

      lock (LockObject) 
      {      
       _repository.Add(record); 
      } 

      if (index % 100 == 0) 
       Console.WriteLine(DateTime.Now + " - Imported " + (index + 1) + " Records.."); 
     } 
    } 
+0

這可能是正確的答案。對於OP,還要注意@chibacity在他的評論中也說了些什麼。按照您的方式進行多線程確實沒有任何好處。也許評論應該被轉移到答案中以獲得更多的知名度。 – Davy8 2010-12-13 21:35:03

+0

我很驚訝沒有人給過這個+1。 – Davy8 2010-12-13 22:11:13

+0

謝謝戴維,但我得到一個錯誤while(index = Interlocked.Increment(ref _counter) zSynopsis 2010-12-13 22:32:44