如果在枚举期间替换列表实例会发生什么?

时间:2015-09-05 23:23:14

标签: c# multithreading

让我们说我们有两个线程在运行,第一个是枚举列表,比如

foreach(int a in someList)
{
    //Do something
    //Thread 2 comes in
}

然后线程2将新实例分配给“someList”

someList = new List<int>();
问题1:thread1会发生什么?它会抛出异常还是继续枚举旧实例?
问题2:在枚举完成之前,是否存在“旧”列表收集垃圾的危险?因为除了枚举器之外什么也没有引用那个旧列表。

2 个答案:

答案 0 :(得分:1)

Foreach在GetEnumerator上调用someList,从而获得IEnumerator类型的对象。此对象会记住原始列表,并逐个从中读取元素。

如果你现在为someList分配新东西,那么:

  • 您只会重新分配变量,不会销毁旧列表,不会更改旧列表
  • 由于旧列表的内容未更改
  • ,因此不会抛出异常
  • 只要它存在( - >直到foreach结束或break s),IEnumerator仍会记住旧列表并从中读取
  • foreach持有IEnumerator,IEnumerator持有旧列表,什么都不会是GC'ed

证明:

using System;
using System.Collections.Generic;

static void Main()
{
    var list = new List<int>{ 1,2,3,4,5};
    foreach(var x in list) // foreach grabs old list and keeps it
    {
        Console.WriteLine(x);
        list = new List<int>{9,9,9}; // replace the whole list
    }
}

输出:

1
2
3
4
5

以上示例是单线程的,其行为与描述的相似。将其更改为多线程不会使事情变得更糟,因为foreach读取变量并记住实例一次,因此在多线程设计中,即使您没有添加锁并离开比赛,在最坏的情况下要么抓住旧列表,要么抓住新列表,但绝不会同时使用。

答案 1 :(得分:0)

问题1:如果我的记忆正常,它会引发异常。

问题2:也许,但这无关紧要,因为无论如何都应抛出异常。

如果您正在修改列表,上面的内容似乎是正确的,我的不好。

相关问题