2015-05-14 53 views
2

我想知道這是否是在下面的代碼中計算x的安全方法。LINQ中的C#並行性

public static IEnumerable<Object> Parse(Object obj, ref int x) 
{ 
    x += 10; 
    return new List<Object>(); 
} 

public static void Query(List<Object> obj) 
{ 
    int x = 0; 

    var result = obj 
     .AsParallel() 
     .Select(o => Parse(o, ref x)) 
     .Aggregate((a, b) => a.Concat(b)); 
} 

這是我的代碼的縮寫版本。我想x是某種static counter for all parallel executions of Parse。我希望這不會讓人困惑。

+1

你確定你確實需要它嗎?你需要什麼?正確同步訪問價值的行爲意味着性能大幅降低,因爲操作無法再完全並行工作。 – Servy

+2

此外,您不應該使用'Aggregate/Concat'將序列序列轉換爲平坦序列。它導致查詢嵌套N層深;它會很快破裂。只要使用'SelectMany',它就是專門設計用來做到這一點的。 – Servy

+0

我真的很需要它,因爲最後我需要檢查所有的Parse方法是否計算了一個確切數量的(非常複雜的)對象。並非常感謝你關於'SelectMany'的提示! –

回答

4

絕對並不安全。 您需要使用Interlocked類。

public static IEnumerable<Object> Parse(Object obj, ref int x) 
{ 
    Interlocked.Add(ref x, 10); 
    return new List<Object>(); 
} 
4

您的代碼有競爭條件。儘管變量x是通過引用傳遞的,但它在所有併發執行中都保持相同的變量,因此向它添加十個必須是原子的。解決這一問題將使用Interlocked.Add方法而不是+=

方式一:

public static IEnumerable<Object> Parse(Object obj, ref int x) 
{ 
    Interlocked.Add(ref x, 10); 
    return new List<Object>(); 
} 
2

我會建議不同的方法來解決這個問題,正如前面建議引入在並行代碼中的同步結構會影響它的工作,如果你還需要那麼你原來的代碼需要像互鎖/鎖使其線程安全的,但是

一個更好的方式是每個線程擁有一個本地計數器和聚集,在年底,是這樣的:

public class MyClass 
{ 
    public int x; 
    public object o; 
} 

public static IEnumerable<MyClass> Parse(Object obj) 
{ 
    MyClass c = new MyClass(); 
    c.x += 10; 
    c.o = <some new object> 
    // Add c to instance of List<MyClass> 
    return new List<MyClass>(); 
} 

public static void Query(List<Object> obj) 
{   
    var result = obj 
     .AsParallel() 
     .Select(o => Parse(o)) 

    // result is of type IEnumerable<MyClass> 

    var sum = result.Sum(a=>a.x); 

    var aggregate = result.Aggregate((a, b) => a.o.Concat(b.o)); 
} 

這是沒有鎖/同步免費的解決方案perfor mance命中,沒有比賽條件。總是用於線程,嘗試爲線程創建局部變量,然後爲每個單獨的線程變量應用一個像sum一樣的函數。

+0

偉大的優化建議! – Amit

+0

這非常聰明!但是你怎麼看待創建元組然後聚合它們? –

+0

任何你喜歡的DS,雖然需要檢查適用性,但保持它的本地線程,除了併發類型沒有DS默認線程安全,並且會導致競爭條件或腐敗 –