读取和写入多线程共享数组

时间:2015-09-16 16:48:40

标签: c# multithreading concurrency thread-safety task-parallel-library

我有以下代码:

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: 2076
  • Result: 1771
  • Result: 0

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

如何以线程安全的方式同时运行上述代码?

2 个答案:

答案 0 :(得分: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个数组项,并且(困难的部分)锁定它们,使得不会发生死锁。即使你想出了一个解决方案,所有锁的开销实际上可能会使代码慢于非并发执行。

答案 1 :(得分:0)

您应该考虑在Systems.Collections.Concurrent命名空间中使用BlockingCollection

如果BlockingCollection不能满足您的需求,则此命名空间有许多不同类型的集合,这些集合都是线程安全的,如字典,堆栈,队列等。