阻塞全局迭代器是否有意义?

时间:2015-02-26 06:43:59

标签: c# multithreading thread-safety

我需要做一些要求读取线程中arrey的每个元素的东西。 每个线程只需要读取元素[i]并执行i++。我需要锁定i++吗?

我在某处读到了带有双变量的异步操作需要锁定以防止意外结果。我怎么能对另一个操作说同样的话?以下是有问题的操作列表吗?

更多细节。我有全局变量:

public static int[] IntArray;
public static int ArrayPointer = 0;

我以这种方式在池中启动一些线程:

ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadMethod));

像这样:

static void ThreadMethod() {
  //loop until ArrayPointer is too big
  Console.WriteLine(IntArray[ArrayPointer]);
  ArrayPointer++;
  //end of loop
}

2 个答案:

答案 0 :(得分:5)

这里重点是你有一个共享变量,++运算符执行读/修改/写操作。该操作原子,即使它的每个部分都是。

所以你可以轻松拥有:

Thread 1                 Thread 2

Read ArrayPointer (0)   
                         Read ArrayPointer(0)
Increment locally (1)
                         Increment locally (1)
Write ArrayPointer (1)
                         Write ArrayPointer (1)

所以尽管有两个增量,你的结果是1。

您可以使用Interlocked.Increment以原子方式递增,但您仍然希望在循环的其余迭代中使用该递增操作的结果,而不是多次读取该变量。

我实际上会在这里提出三件事:

  • 不要使用静态变量。全局州使代码难以测试且难以推理
  • 将线程方面与其他任何方面隔离开来 - 将其封装在具有更高级别行为的类中。如果你真的需要全局状态,那么你可以拥有该类类型的静态只读变量,如果你愿意的话。
  • 尽量不要做太多低级别的事情。 .NET提供了大量的并发编程选项,无论是“核心”TPL(以async / await形式提供语言支持)还是构建在其上的TPL Dataflow之类的东西。这些是由人们编写的构建块,其主要工作是使这种东西变得坚固和高效。不要试图自己做他们的工作:)

答案 1 :(得分:1)

该死的,这是非常明显的,因为另一个线程可以突然执行i++并且数组的某些元素将被滑落。

所以只是:

static void ThreadMethod() {
  //loop until ArrayPointer is too big
  lock(LockObject) {
      Console.WriteLine(IntArray[ArrayPointer]);
      ArrayPointer++;
  }
  //end of loop
}