2016-10-04 78 views
0

具有以下代碼,我如何更新共享變量?Parallel.ForEach不更新共享變量

 List<Person> list = new List<Person> {new Person {Age = 1}, new Person {Age = 2}, new Person {Age = 5}}; 
     long total = 0; 

     Parallel.ForEach(list,() => 0, (person, loop, subtotal) => 
      { 
       Add(person, subtotal); 
       return subtotal; 
      }, 

      finalResult => Interlocked.Add(ref total, finalResult) 
     ); 

    public static void Add(Person person, int shared) 
    { 
     // Do some work 
     shared =+ person.Age; 
    } 

對於共享某種原因,回來爲0

+0

即使修復'ref小計問題',這也不會起作用。你有一個非常明顯的競爭條件。你正在不斷更新和閱讀價值。 – Jonesopolis

+0

那我該如何跟蹤一個共享變量呢? – BobSwanson

+1

@BobSwanson首先不要將總和並行化。這會比僅僅在一個線程中彙總值慢得多。除了更快,它也會更簡單,更容易出錯,更易於維護等。 – Servy

回答

2

共享返回爲0,因爲它是作爲0發送的,並且是按值傳遞的。您需要使用ref關鍵字,或以其他方式解決此行爲(靜態變量)。

public static void Add(Person person, ref int shared) 
{ 
    // Do some work 
    shared =+ person.Age; 
} 

它看起來像你也有一個問題,你正在使用'= +'來代替'+ ='。

public static void Add(Person person, ref int shared) 
{ 
    // You likely meant to do this. 
    shared += person.Age; 
} 
+0

現在它返回最後一個..不是所有年齡的總和 – BobSwanson

+0

大概你打算使用+ =?正如你設置分享到Age的正面價值? –

+0

啊,那工作,它需要鎖嗎?因爲它是共享的? – BobSwanson

0

同樣的原因,將不會在「常規」 C#代碼工作...整數是值類型,所以你需要做的參數REF參數。否則,你只是遞增本地副本。此外,您應該使用Interlocked.Increment而不是+ =,否則可能會遇到線程問題,因爲+ =不一定是原子。

2

更改了一下你的代碼,你會預期的結果:

static void Main(string[] args) 
{ 
    List<Person> persons = new List<Person> 
    { 
     new Person { Age = 1 }, 
     new Person { Age = 2 }, 
     new Person { Age = 5 } 
    }; 

    long total = 0; 

    Parallel.ForEach(persons, person => Add(person, ref total)); 

    Console.WriteLine(total); 
    Console.ReadKey(); 
} 

public static void Add(Person person, ref long shared) 
{ 
    // since here you access a shared variabe, we 
    // can use the Interlocked class in order our operation 
    // to be atomic. 
    Interlocked.Add(ref shared, person.Age); 
} 
+0

總有誤。這是不同的每一次 – BobSwanson

+0

@BobSwanson你是正確的!請參閱我的編輯。 – Christos

+0

如果我的共享不是int或long,而是一個小數,我會用lock()來代替嗎? – BobSwanson

0

試試這個

int sum = list.AsParallel().Sum(person => person.Age); 

結果將是相同的,更少的代碼將被使用。