Task.WaitAll似乎有问题

时间:2019-05-31 16:09:31

标签: c# task-parallel-library

我正在开发执行各种数学运算的仿真工具。到目前为止,我还不需要进行并行操作,但是现在我确实需要它们。在各种并行方法中,我读到应该使用Tasks获得最佳性能。

我写了这个简单的程序,但是我意识到有些问题。

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
[...]

1 个答案:

答案 0 :(得分:0)

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

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

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