GC如何使用IEnumerator和yield?

时间:2016-02-12 10:22:50

标签: c# garbage-collection

我知道枚举器和yield关键字可用于帮助执行异步/交错操作,因为您可以调用MoveNext()来运行下一个代码块。

但是,我并不真正了解Enumerator对象是什么。使用枚举器范围的内存在哪里?如果您不是MoveNext()一名调查员,那么它最终会得到GC吗?

基本上,我试图保持我的GC命中率,因为我可能使用大量的枚举器和GC可能是Unity内部的一个问题,特别是由于它使用的旧版Mono。

我试图对此进行分析,但仍然无法绕过它们。我不理解枚举器发生的范围/引用。当你从一个产生函数的函数创建枚举器时,我也不明白枚举器是否被创建为对象。

以下示例更好地展示了我的困惑:

// Example enumerator
IEnumerator<bool> ExampleFunction()
{
    SomeClass heavyObject = new SomeClass();
    while(heavyObject.Process())
    {
        yield return true;
    }

    if(!heavyObject.Success)
    {
        yield return false;
    }

    // In this example, we'll never get here - what happens to the incomplete Enumerator
    // When does heavyObject get GC'd?
    heavyObject.DoSomeMoreStuff();
}

// example call - Where does this enumerator come from? 
// Is something creating it with the new keyword in the background?
IEnumerator<bool> enumerator = ExampleFunction();
while(enumerator.MoveNext())
{
    if(!enumerator.Current)
    {
        break;
    }
}

// if enumerator is never used after this, does it get destroyed when the scope ends, or is it GC'd at a later date?

3 个答案:

答案 0 :(得分:5)

您可能应该阅读枚举器内部帖子。从内部来看,你可以回答所有这些问题。

速成课程:每个迭代器方法执行都返回一个新的枚举器对象。局部变量成为字段。

如果没有人再使用该枚举器对象,则它有资格进行收集。此外,局部变量创建的所有引用都会因GC而消失。

当确切的局部变量停止为GC引用时,存在一些边缘情况。如果您的调查员相当短暂,这并不重要。

CLR不知道枚举器是什么。它只是由C#编译器生成的一个类。

将xanatos的链接拉到这个答案中,因为它的说明并且他似乎没有发布答案:http://goo.gl/fs4eNo

答案 1 :(得分:3)

  

//如果在此之后从未使用过枚举器,那么它是否会被销毁   范围结束

正确

收集资格没有参考资料。对于GC的范围界定,调查员没有什么特别之处。当调查员以超出任何其他类型的范围时,以同样的方式处理/清除。

答案 2 :(得分:2)

枚举器的管理方式与每个其他托管实例的管理方式相同 - 超出范围时。因此,如果从未调用MoveNext(),那么当枚举数超出其范围时,GC将删除(或更好地将其标记为删除)。迭代枚举器不会以任何并行方式进行,因此执行和垃圾收集在完成后都会确定性地和连续地运行。

当在迭代器方法中时,yield-return - stament根本不是最后一个语句,它只是意味着当调用MoveNext()时,应该返回当前结果。但是,yield return后面的所有内容都会在调用MoveNext之后运行,因此您也会调用DoSomeMoreStuff

但是heavyObject具有方法的范围,因此它与迭代器一样长 - 迭代器是while - 循环中的块。这意味着如果您的迭代器甚至没有返回任何实例heavyObject将被立即处理,否则迭代完成后。