C#闭包和值类型的注意事项
2017年3月22日
2764
今天在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)
难道调用ThreadPool.QueueUserWorkItem()的时候,真是必须调用Thread.Sleep(N)吗?