2016-10-04 63 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); 

结果将是相同的,更少的代码将被使用。