【问题标题】:Reading and writing a multi-thread shared array读写多线程共享数组
【发布时间】:2015-09-16 16:48:39
【问题描述】:

我有以下代码:

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 变量)

如何使上述代码以线程安全的方式并发运行?

【问题讨论】:

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

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


【解决方案1】:

中的值变量

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

【讨论】:

    【解决方案2】:

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

    如果 BlockingCollection 不能满足您的需求,这个命名空间有一堆不同类型的集合,它们都是线程安全的,例如字典、堆栈、队列等。

    【讨论】:

    • 这看起来很有趣!如何将我的二维数组映射到一维 BlockingCollection
    • 最好的方法可能是拥有一个BlockingCollections的数组
    • 您将如何重新制定我正在创建帕斯卡三角形的第一个 For 循环?我需要设置特定的索引,并且一个集合是顺序的
    • 此外,似乎从BlockingCollection&lt;T&gt; 中获取一个元素实际上会将其从集合中删除,有没有办法只获取一个对象并将其保留在那里?
    猜你喜欢
    • 1970-01-01
    • 2013-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-13
    • 1970-01-01
    • 2010-12-25
    • 2014-02-02
    相关资源
    最近更新 更多