0

多线程我有以下代码:读写共享阵列

const int MAX = 101; 
const int MIN_COMBINATORIC = 1000000; 

int[,] pascalTriangle = new int[MAX, MAX]; 

Parallel.For(0, MAX, i => 
{ 
    pascalTriangle[i, 0] = 1; 
    pascalTriangle[i, i] = 1; 
}); 

int counter = 0; 

Parallel.For(0, MAX, x => Parallel.For(1, x, y => 
{ 
     int value = pascalTriangle[x - 1, y] + pascalTriangle[x - 1, y - 1]; 
     Interlocked.Exchange(ref pascalTriangle[x, y], value < MIN_COMBINATORIC ? value : MIN_COMBINATORIC); 
     if(value > MIN_COMBINATORIC) 
      Interlocked.Increment(ref counter); 
})); 

Console.WriteLine("Result: {0}", counter); 

的问题是,有时打印出正确答案(Result: 4075),但有时它打印一个随机的(和错误的)答案,如:

  • Result: 1771
  • Result: 0

我猜这与我正在读写多线程之间共享数组的事实有关。 正如你所看到的,我尝试添加Interlocked.Exhange()线程安全的写操作,但我无法找到一个类似的方法,用于读取(有一个Interlocked.Read()但它只能读取long变量)

我怎样才能让上面的代码以线程安全的方式并发运行?

+0

它可以通过使用一些同步原语使线程安全,但我不确定它会工作,因为它似乎依赖于以前的计算,并行运行时可能不完整。 –

回答

0

您应该在Systems.Collections.Concurrent名称空间中使用BlockingCollection进行调查。

如果BlockingCollection不适合您的需要这个命名空间包含了许多都是线程安全的像字典一样,栈,队列等

+0

这似乎很有趣!我如何将我的二维数组映射到一维'BlockingCollection'? –

+0

最好的方法可能是有一个'BlockingCollections'的数组 – rmn36

+0

你将如何重新构造第一个'For'循环,我创建了pascal三角形?我需要设置特定的索引,并且一个集合是连续的 –

3

变量的值在不同类型的集合的

int value = pascalTriangle[x - 1, y] + pascalTriangle[x - 1, y - 1]; 

依赖于数组中的两个项目。由于并发性,这些值可以在添加这两个项目之间改变,将它们存储到value中,并将该值传递到Interlocked.Exchange。这意味着,当您将value存储在pascalTriangle[x, y]时,pascalTriangle[x - 1, y]和/或pascalTriangle[x - 1, y - 1]中的值可能已更改。

我实际上想不出一个简单的并发解决方案。显而易见的解决方案是不要同时做到这一点。如果这是你想要使用的实际代码,那么同时运行它并没有真正的好处,因为它只循环101 * 101 = 10,201次,这可以很快完成(初始化三角形的第一个并行代码只是循环101次,所以也可以是单线程的)。如果您创建了多个这样的三角形,那么您可能会从创建三角形的同时获益(换句话说,创建三角形的此方法是单线程的,但调用方在单独的线程上多次调用此方法)。

如果你真的想要一个并发解决方案,你需要弄清楚如何实现一个锁定机制,可以锁定你正在访问的3个数组项目,并且(困难的部分)锁定它们,使得死锁不能发生。即使你想出了一个解决方案,所有锁的开销可能实际上使代码比非同时执行代码慢。