多个线程将元素添加到一个列表。 为什么列表中的项目总是少于预期?

以下代码解释了我的问题。 我知道列表不是线程安全的。 但是这个潜在的“真实”原因是什么?

class Program { static void Main(string[] args) { List strCol = new List(); for (int i = 0; i  { AddElements(strCol); }).ContinueWith((t) => { WriteCount(strCol, id.ToString()); }); } Console.ReadLine(); } private static void WriteCount(List strCol, string id) { Console.WriteLine(string.Format("Task {0} is done. Count: {1}. Thread ID: {2}", id, strCol.Count, Thread.CurrentThread.ManagedThreadId)); } private static void AddElements(List strCol) { for (int i = 0; i < 20000; i++) { strCol.Add(i.ToString()); } } } 

我将跳过明显的答案“列表不是线程安全的” – 你已经知道了。

列表项保存在内部数组中。 将项目添加到List时,至少有两个阶段(从逻辑的角度来看)。 首先,List获取一个索引,指示放置新项目的位置。 它使用此索引将新项目放入数组中。 然后它递增索引,这是第二阶段。 如果第二个(或第三个,第四个……)线程同时添加新项目,则可能会有两个(3,4,…)新项目放入索引之前的同一个数组位置由第一个线程递增。 项目被覆盖并丢失。

添加新项目和递增索引的内部操作必须始终一次完成,以使列表成为线程安全的。 这就是所谓的关键部分。 这可以通过锁来实现。

希望这能解释一下。

这是因为List不是线程安全的。

您应该为此使用线程安全集合,例如System.Collections.Concurrent中的一个集合。 否则,你需要同步对List所有访问(即:将每个Add调用放在一个锁中),这将完全不使用多个线程调用它的目的,因为你没有做任何其他的工作在这种情况下。