2010-05-02 74 views
6

我遇到一些涉及Parallel for循環並添加到列表的問題。問題是,相同的代碼可能在不同的時間產生不同的輸出。我已經在下面設置了一些測試代碼。在這段代碼中,我創建了一個包含10,000個int值的列表。 1/10的值將爲0,1/10的值將爲1,一直到1/10的值爲9.並行For循環 - 添加到列表時出現問題

設置此列表後,我設置了一個Parallel for循環遍歷列表。如果當前的數字是0,我將一個值添加到一個新的列表。 Parallel for循環完成後,我輸出列表的大小。尺寸應該始終爲1,000。大多數時候,答案都是正確的。然而,我看到3點可能的不正確的結果發生:

  1. 列表的大小小於1,000
  2. 一種IndexOutOfRangeException發生@doubleList.Add(0.0);
  3. 一種ArgumentException發生@doubleList.Add(0.0);

該消息給出的ArgumentException是:Destination array was not long enough. Check destIndex and length, and the array's lower bounds.

什麼可能導致錯誤S'這是.Net錯誤嗎?有什麼我可以做,以防止這種情況發生?

請爲自己嘗試一下。如果您沒有收到錯誤,請嘗試幾次。另請注意,使用單核機器可能不會看到任何錯誤。

using System; 
using System.Collections.Generic; 
using System.Threading.Tasks; 

namespace ParallelTest 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      List<int> intList = new List<int>(); 
      List<double> doubleList = new List<double>(); 

      for (int i = 0; i < 250; i++) 
      { 
       intList.Clear(); 
       doubleList.Clear(); 

       for (int j = 0; j < 10000; j++) 
       { 
        intList.Add(j % 10); 
       } 

       Parallel.For(0, intList.Count, j => 
       { 
        if (intList[j] == 0) 
        { 
         doubleList.Add(0.0); 
        } 
       }); 

       if (doubleList.Count != 1000) 
       { 
        Console.WriteLine("On iteration " + i + ": List size = " + doubleList.Count); 
       } 
      } 

      Console.WriteLine("\nPress any key to exit."); 
      Console.ReadKey(); 
     } 
    } 
} 
+1

什麼更可能是.NET錯誤或代碼中的錯誤? – 2010-05-02 03:50:30

+0

調試規則1:錯誤總是在你的代碼中。你可以從* title *這個... – Aaronaught 2010-05-02 03:58:20

回答

18

我希望System.Collections.Generic.List是不是線程安全的,也就是說如果你試圖從兩個不同的線程同時Add,事情會出問題。啊,是的,它在docs中這麼說。

你可以防止這種情況在許多方面發生了:

  • 使用集合類型,允許線程增加(有一些new ones在.NET 4.0中)
  • 鎖添加之前
  • 使用線程本地存儲的集合,並在年底
  • 將它們合併...

這些是數據並行代碼遇到的非常典型的問題。

+1

+1中判斷出了什麼問題。 System.Collections.Generic.List不是線程安全的。 .NET Framework 4.0中的線程安全集合 – 2010-05-02 03:51:28

+0

+1對於您的第三個建議可能比其他兩個建議快得多。 – tster 2010-05-02 03:54:03

+0

@tster - 是的,甚至有一個Parallel.For超載,以幫助該策略:http://msdn.microsoft.com/en-us/library/dd783586.aspx – Brian 2010-05-02 03:57:12