2015-02-17 47 views
0

正確的操作繼我以前question的IEnumerable的<foo>

在多線程程序,不同的線程的每個產生一個很長的結果的列表。當線程完成了它的任務,我想串聯不同列表到一個列表。請介意如下:

public struct AccEntry 
{ 
    internal AccEntry(int accumulation) 
     : this() 
    { 
     Accumulation = accumulation; 
    } 
    public int Accumulation { private set; get; } 
} 


internal class Functions 
{ 
    internal Functions(Object lockOnMe, IEnumerable<AccEntry> results) 
    { 
      _lockOnMe = lockOnMe; 
      _results = results; 
      _result = new List<AccEntry>(); 
    } 

    private IEnumerable<AccEntry> _results { set; get; } 
    private List<AccEntry> _result { set; get; } 

    internal void SomeFunction() 
    { 
      /// some time consuming process that builds _result 
      lock(_lockOnMe) 
      { 
       /// The problem is here! _results is always null. 
       if (_results == null) _results = _result; 
       else _results = _results.Concat(_result); 
      } 
    } 
} 

public class ParentClass 
{ 
    public void DoJob() 
    { 
      IEnumerable<AccEntry> results = null; 
      /// initialize and launch multiple threads where each 
      /// has a new instance of Functions, and call SomeFunction. 
    } 
} 

的問題,因爲在代碼中提到,是_results總是。當線程改變其設置爲_result其他線程發現它一次。我也嘗試過使用參考關鍵字功能構造函數爲結果,但它沒有改變任何東西。

假設以下按預期執行,我想這可能是我錯過上述代碼的意義呢?!

List<int> listA = new List<int>(); 
List<int> listB = new List<int>(); 
listA.Add(10); 
listB.Add(12); 
IEnumerable<int> listC = null; 
listC = listA; 
listC = listC.Concat(listB); 
+0

你永遠更新是在方法DoJob(),因爲您是按值將它傳遞給函數的構造函數中定義的變量「結果」。一種可能的解決方案是將其初始化爲新的List()而不是null。 – 2015-02-17 23:06:08

+0

@AugustoBarreto我也試過ref關鍵字,它沒有工作。初始化由一個*新List()*迫使我使用* AddRange *而不是* Concat *,這在我的應用程序中被認爲是一個高性能損失。 – Hamed 2015-02-18 07:12:56

回答

1

當你在串聯中的項目並分配回給_results變量,將替換您分配給該變量的初始值。這個新的物品集合將在這個實例中是本地的。

而不是使用IEnumerable<>,你必須更換以更新它,使用List<>,你可以在地方項目添加到:

internal class Functions 
{ 
    internal Functions(Object lockOnMe, List<AccEntry> results) 
    { 
      _lockOnMe = lockOnMe; 
      _results = results; 
      _result = new List<AccEntry>(); 
    } 

    private object _lockOnMe; 
    private List<AccEntry> _results; 
    private List<AccEntry> _result; 

    internal void SomeFunction() 
    { 
      /// some time consuming process that builds _result 
      lock(_lockOnMe) 
      { 
       _results.AddRange(_result); 
      } 
    } 
} 

只要確保創建Functions實例之前創建列表。

+0

由於性能損失,列表被有意避免。 AddRange需要大約1秒來連接兩個列表,每個列表中有500萬個項目,其中Concat在幾毫秒內執行相同的操作。你認爲可能有任何解決方法來使用Concat而不是AddRange? – Hamed 2015-02-18 07:10:59

+0

@Hamed:性能測試是比較蘋果和橘子。 'Concat'方法實際上並沒有連接集合,它創建了一個將在迭代時連接集合的查詢。 'Concat'調用速度非常快,因爲它不會執行實際的工作,稍後迭代結果時會執行此操作。如果你真的意識到串聯的集合,你會看到「AddRange」快了大約十倍。 – Guffa 2015-02-18 08:06:17

+0

我剛剛意識到* Concat *的意義。當你想迭代時,它需要所有連接的引用都是活着的,這在我的場景中是不可接受的。 – Hamed 2015-02-18 08:12:18

相關問題