C# 为什么任务会给出不同的Linq Sum()输出?

C# 为什么任务会给出不同的Linq Sum()输出?,c#,linq,C#,Linq,下面的代码很奇怪,它给了我一个可枚举范围的不同的Sum 为什么? 预期输出: Sum 2: 0 Sum 3: 0 Sum 2: 94646670 Sum 3: 0 输出: Sum 2: 0 Sum 3: 0 Sum 2: 94646670 Sum 3: 0 列表不是线程安全的,因此并行。调用发生在不同的线程上。您的答案是不确定的,因为线程上调用num.Sort()正在修改列表,而另一个正在枚举列表。每当运行并行任务时,两个任务都有同时访问同一内存的风险(假设您没有序列化任务的Sync

下面的代码很奇怪,它给了我一个可枚举范围的不同的
Sum

为什么?

预期输出:

Sum 2: 0
Sum 3: 0
Sum 2: 94646670
Sum 3: 0 
输出:

Sum 2: 0
Sum 3: 0
Sum 2: 94646670
Sum 3: 0 

列表
不是线程安全的,因此
并行。调用
发生在不同的线程上。您的答案是不确定的,因为线程上调用
num.Sort()
正在修改列表,而另一个正在枚举列表。

每当运行并行任务时,两个任务都有同时访问同一内存的风险(假设您没有序列化任务的SynchronizationContext,或者没有门访问的
锁)。在这种情况下,一个线程在列表上迭代,而另一个线程在该列表中的元素上移动。因此,总和可能多次包含某些元素,而其他元素则完全不包含。在某些情况下(本例中不包含)线程甚至可能读取元素的一部分,例如,如果它读取的元素的大小超过原子读取的宽度(在Wintel上为32位对齐)

为了证明这是原因,请尝试创建第二个列表并将其交给第二个任务,如下所示:

var num1 = Enumerable.Range(-10000, 20001).Reverse().ToList();
var num2 = Enumerable.Range(-10000, 20001).Reverse().ToList();

Action task1 = () => 
{
    Console.WriteLine("Sum 2: {0}", num1.Sum());
};

Action task2 = () =>
{
    num2.Sort();
    Console.WriteLine("Sum 3: {0}", num2.Sum());
};

Parallel.Invoke(task1, task2);

如果这样做,两个答案都会得到
0

Sort
Sum
不是原子的…一个线程在计算和,而Sort在移动数字,因此
Sum
将对一些数字进行两次计数,而一些数字将被完全跳过。您会注意到,实际上您是在计算和每次运行代码时都会得到不同的结果。

排序会改变状态,因此使用
OrderBy(x=>x)
而不是
排序()
您将获得可预测的排序结果。

task2
num
列表进行排序,而
task1
对其进行迭代。因此,您看到的行为是预期的。要修复,请确保
task1
task2
未共享状态(即不共享
num
)@KhawajaAsim唯一修改num的部分是Sort。还可以是什么?“你认为排序效果如何?”-结合并行。调用:是的,当然。@KhawajaaAsim让我们假设我有10个孩子。你问他们的名字。我把他们排成一行。我读他们写。他们叫Sally,Bob,Charlie,Deirdre等。然后我做同样的事,但我做这件事的时候孩子们一直在交换位置。现在他们叫Sally,Bob,Charlie,Sally等等。当你的
Sum
调用在列表上迭代时,也会发生同样的事情。@mjwills这是一个很好的解释,谢谢,他没有修改集合,这让他有点惊讶exception@Steve例如,如果列表的大小发生变化,那么这种情况更可能发生。谢谢你的回答,我只是想问一下为什么会这样,谢谢你