C#闭包和值类型的注意事项

今天在C#闭包程序中遇到了和自己预期不一致的程序行为。进行了一系列挖掘。

正常代码:

for (int i = 0; i < 5; i++)
    Console.WriteLine(i);

输出01234。

多线程代码1:

for (int i = 0; i < 5; i++)
    ThreadPool.QueueUserWorkItem(_ => Console.Write(i));

本来期待的输出是01234,但是得到的是55555。(新老版本结果一致)

多线程代码2:

foreach(var o in new[] { 0,1,2,3,4})
    ThreadPool.QueueUserWorkItem(_ => Console.WriteLine(o));

这个在老版本.net中输出是55555,在新版本.net (4.6)输出是01234。

单线程代码3:

Action[] a = new Action[5];
for (int i = 0; i < 5; i++)
    a[i] = () => Console.WriteLine(i);
foreach (var b in a)
    b();

输出55555。

单线程代码4:

for (int i = 0; i < 5; i++)
{
    Action b = () => Console.WriteLine(i);
    b();
}

输出是01234。

新老版本.net对数组的枚举器,闭包的封包有不一致的行为,老版本.net会调用最后一个枚举器对象。新版本.net会对每个枚举器进行独立封包。但是对for循环的循环变量,新老版本均会封包最终的变量。

参考链接:

Why lambdas seem broken in multithreaded environments (or how closures in C# works)

 .NET:C# 如何实现的闭包?

难道调用ThreadPool.QueueUserWorkItem()的时候,真是必须调用Thread.Sleep(N)吗?