最近有一个隐藏的BUG,是因为使用匿名函数导致的。
要重现该BUG,可以先查看如下两个程序。
第一个:
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
DisplayInt(i);
}
Console.Read();
}
private static void DisplayInt(int i)
{
MethodInvoker mi = new MethodInvoker(delegate()
{
Thread.Sleep(1000);
Console.Write(i);
});
mi.BeginInvoke(null, null);
}
}
输出为:0 1 2 …… 9
第二个程序,采用匿名函数的方式来完成相同的功能:
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
MethodInvoker mi = new MethodInvoker(delegate()
{
Thread.Sleep(1000);
Console.Write(i);
});
mi.BeginInvoke(null, null);
}
Console.Read();
}
}
结果发现,事情并不是我们想象的那样,而是
输出为:10 10 10 …… 10
分析其原因,在第二个程序中,虽然匿名函数和第一个程序中的DisplayInt完成的是一样的功能。但是这个功能本身是要新起一个线程来执行打印i。而i这个参数,是由主线程传入进去的。那么,可以理解为,在第二个程序中,主线程中的FOR循环已经执行完毕的时候,FOR循环所起的10个工作线程,还没有启动,当它们由.NET运行时环境自行决定启动的时候(当然,这个事件会很短),FOR循环中的i已经变成10,所以,第二个程序产生了上面的结果。
当然,我想,这个结果应该也不唯一,这完全取决于匿名函数中的线程执行的时候FOR循环是否已经执行完毕了。
回过头来,考虑第一段代码,因为不是使用的匿名函数,所以内存地址中,保留了一个i的副本,故程序执行正确。