猜一下......当i == 0时,这个程序需要多长时间才能生成第一个输出?它应该是即时的,对吧?通过对yield
的懒惰评估,它应该在此之后快速连续产生输出,对吗?
static void Main(string[] args)
{
Stopwatch stopwatch = Stopwatch.StartNew();
int i = 0;
foreach (var item in massiveYieldStatement())
{
if (i++ % 10000 == 0)
Console.WriteLine(stopwatch.ElapsedMilliseconds / 1000);
}
Console.ReadKey();
}
static IEnumerable<string> massiveYieldStatement()
{
yield return "a";
yield return "a";
.. repeat 200,000 times !!
yield return "a";
}
但事实并非如此!它在那里没有输出4到21分钟,然后快速完成 - 在一个案例中60ms以下!在这几分钟内,使用了一个核心CPU的100%并且内存使用量增加。在我遇到这种情况的实际场景中,在第一次迭代发生之前抛出了Stackoverflow
异常!我在Visual Studio中以调试模式尝试了它,并在命令提示符下以发布模式尝试了它。我在Windows 7 x64和Windows Server 2008 R2 x64上尝试过它。
有人能解释这里发生了什么吗?它会为你重复吗?
注意:这不是真正的代码:真正的代码具有更少的yield语句,但要复杂得多。这只是最简单的复制品。
答案 0 :(得分:4)
此代码生成的程序集大几MB。 yield return
是一个特殊的野兽,因为它看起来很简单,但C#编译器实际上生成了一个类('state machine')来实现massiveYieldStatement方法。我很确定你正在等待JIT编译器编译该类的MoveNext()方法(你可以用ildasm验证它:如果你试图打开MoveNext()方法,它也需要花费很多时间)。
答案 1 :(得分:2)
问题不在于产量,而是返回200K产量的函数(顺便说一下,100K线已经减慢了我的VS)。每当您从MoveNext()
返回的IEnumerator
上执行第一次IEnumerable<string>.GetEnumerator
时,需要对其进行评估并生成新的州级。
static IEnumerable<string> massiveYieldStatement()
{
for(int i = 0; i < 200000; ++i)
yield return "a";
}
由于评估很快,按预期运行得很快。