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
,您将获得一致的结果。这两个操作使用硬件支持来保证原子性。
我正在开发一个执行各种数学运算的模拟工具。到目前为止我还没有做并行操作的需要,但现在我真的需要它们。在我读到的各种并行方法中,应该使用任务来实现最佳性能。
我写了这个简单的程序,但我意识到出了点问题。
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
,您将获得一致的结果。这两个操作使用硬件支持来保证原子性。