Task.WaitAll 似乎有问题

Task.WaitAll seems bugged

我正在开发一个执行各种数学运算的模拟工具。到目前为止我还没有做并行操作的需要,但现在我真的需要它们。在我读到的各种并行方法中,应该使用任务来实现最佳性能。

我写了这个简单的程序,但我意识到出了点问题。

private static int taskCounter { get; set; }
private static void SimpleTest() { taskCounter--; }

static void Main(string[] args)
{
    for (int N = 0; N < 100; N++)
    {
        taskCounter = 0;
        List<Task> taskList = new List<Task>();
        for (int i = 0; i < 100; i++)
        {
            taskCounter++;
            Task task = Task.Factory.StartNew(() => SimpleTest());
            taskList.Add(task);
        }
        Task.WaitAll(taskList.ToArray());
        Console.WriteLine("Unsolved: {0}", taskCounter);
    }
}

预期:未解决:所有迭代均为 0。 结果:

[...]
Unsolved: 0
Unsolved: 0
Unsolved: 2
Unsolved: -4
Unsolved: -1
Unsolved: -1
Unsolved: 0
Unsolved: 0
Unsolved: 2
Unsolved: -1
[...]

正如 elgonzo 在他的评论中指出的那样。 taskCounter++taskCounter-- 这两个操作不是原子的,这意味着它们实际上由几个步骤组成(从内存中读取变量,对其加一或减一,然后将结果写回内存。

如果多个线程同时执行这些序列,最终结果可能会变得不正确。 假设线程 1 和线程 2 从内存中读取变量,同时独立地递增它们各自的副本,然后尝试将结果写回内存。在那种情况下,写入内存的最终值与这两个序列一个接一个执行时的值不同。

现代 CPU 的高速缓存使得那些并行执行的操作很可能会产生不正确的结果。如果按照建议使用 Interlocked.Increment / Interlocked.Decrement,您将获得一致的结果。这两个操作使用硬件支持来保证原子性。