obj.Coll
在枚举时发生了变化。如果它没有被当前线程改变,它可能会被其他线程改变。如果集合被当前线程更改,基本上有两种方法可以解决问题:您可以创建集合的副本并枚举副本,或者可以推迟更改,直到枚举集合。
但是,如果集合被另一个线程更改,则应该以线程安全的方式访问集合(不仅在这里,而且在任何地方)。
编辑为基伦斯通:
我写了一个短代码,以证明List<T>.ToArray()
不是线程安全的。
var list = new List<int>();
Task.Factory.StartNew(() => {
for (int i = 0; i < 1000000; ++i) {
list.Clear();
// Add values from 1 to 9
for (int j = 1; j < 10; ++j) {
list.Add(j);
}
}
Console.WriteLine("Thread Exit: list.Add()");
});
Task.Factory.StartNew(() => {
for (int i = 0; i < 100; ++i) {
var array = list.ToArray();
if (array.Length > 0) {
Console.WriteLine("ToArray(): {0}", string.Join(", ", array));
}
}
Console.WriteLine("Thread Exit: list.ToArray()");
});
下面是输出的片段。我想这证明了我的说法。该片段包含15行,其中9个包含错误的数据。
ToArray(): 1, 2, 3, 4
ToArray(): 1, 2, 3, 4, 5, 0, 0, 0, 0
ToArray(): 1, 2, 3, 4, 5, 6, 7, 0, 0
ToArray(): 0, 0, 0, 0, 0, 0, 0, 0, 0
ToArray(): 1, 2, 3, 4, 5, 0, 0, 0, 0
ToArray(): 0, 0, 0, 4, 5, 6, 7
ToArray(): 1, 2, 0, 0, 0, 0, 0, 0, 0
ToArray(): 1, 2, 3
ToArray(): 1, 0, 0, 0, 0, 0, 0, 0, 0
ToArray(): 0, 0, 0, 0, 0, 0, 0, 0
ToArray(): 1, 2
ToArray(): 1, 2, 3
ToArray(): 0, 0, 0, 0, 0, 0, 0, 0
ToArray(): 1, 2, 3, 4
ToArray(): 1, 2, 3, 4, 5, 6, 7, 8
将会有更多的变化,如果我们使用list.Insert(0, j)
代替list.Add(j)
。
“有时候”是发生了什么事情的死亡泄露:另一个线程修改了收集。 – Jon 2011-03-31 09:24:14
[C#Collection可能的重复被修改;枚举操作可能不会执行](http://stackoverflow.com/questions/2024179/c-sharp-collection-was-modified-enumeration-operation-may-not-execute) – nawfal 2013-06-05 17:52:25