在以下教程中:http://www.albahari.com/threading/
他们说以下代码:
for (int i = 0; i < 10; i++)
new Thread (() => Console.Write (i)).Start();
Run Code Online (Sandbox Code Playgroud)
是非确定性的,可以产生以下答案:
0223557799
我认为当使用lambda表达式时,编译器会创建某种匿名类,通过在捕获类中创建类似它们的成员来捕获正在使用的变量.但是i值类型,所以我认为他应该被价值复制.
我的错在哪里?
如果答案将解释闭包如何工作,如何保存特定int的"指针",在这种特定情况下生成了什么代码,那将非常有用?
这里的关键点是闭包关闭变量而不是值.因此,在您关闭它时给定变量的值是无关紧要的.重要的是调用匿名方法时该变量的值.
当您看到编译器将闭包转换为什么时,这很容易看到.它会在道德上创造出类似于此的东西:
public class ClosureClass1
{
public int i;
public void AnonyousMethod1()
{
Console.WriteLine(i);
}
}
static void Main(string[] args)
{
ClosureClass1 closure1 = new ClosureClass1();
for (closure1.i = 0; closure1.i < 10; closure1.i++)
new Thread(closure1.AnonyousMethod1).Start();
}
Run Code Online (Sandbox Code Playgroud)
所以在这里我们可以更清楚地看到发生了什么.该变量有一个副本,该变量现在已被提升为新类的字段,而不是本地变量.现在修改局部变量的任何地方都会修改此实例的字段.我们现在可以看到为什么你的代码打印它的作用.在启动新线程之后,但在它实际执行之前,for主线程中的循环将返回并在闭包中递增变量.闭包尚未读取的变量.
要产生所需的结果,您需要做的是确保不是让循环的每个迭代都关闭单个变量,而是每个迭代都需要一个它们关闭的变量:
for (int i = 0; i < 10; i++)
{
int copy = i;
new Thread(() => Console.WriteLine(copy));
}
Run Code Online (Sandbox Code Playgroud)
现在copy变量在关闭后永远不会改变,我们的程序将打印出0-9(尽管以任意顺序,因为线程可以按照OS的要求进行调度).