阻塞全局迭代器有意义吗?

Do blocking of global iterator make sense?

我需要做一些事情,要求读取线程中数组的每个元素。 每个线程只需要读取元素 [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
}

该死的,这太愚蠢了,因为另一个线程可以突然做 i++ 并且数组的一些元素将被跳过。

所以只是:

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

这里的重点是你有一个共享变量,++ 运算符做了一个 read/modify/write 操作。该操作不是原子的,即使它的每个单独部分都是。

所以你可以轻松拥有:

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 以原子方式递增,但您仍然希望在循环的其余迭代中使用该递增操作的结果,而不是多次读取变量。

我实际上会在这里提出三点建议:

  • 不要为此使用静态变量。全局状态使代码难以测试和推理
  • 将线程方面与其他任何方面隔离 - 将其封装在 class 中以展示更高级别的行为。如果你真的、真的需要全局状态,那么你可以有一个 class 类型的静态只读变量,如果你愿意的话。
  • 尽量不要自己做太多底层的东西。 .NET 有大量的并发编程选项,无论是 "core" TPL(具有 async/await 形式的语言支持)还是构建在它之上的 TPL Dataflow 之类的东西。这些构建块是由那些主要工作是使这类东西坚如磐石和高性能的人编写的。不要试图自己完成他们的工作:)