即使在方法完成后线程如何访问局部变量?

时间:2012-01-23 19:35:31

标签: c# multithreading

说我有像这样的C#方法

public void MyMethod()
    {
        int i = 0;

        var thread = new Thread(() =>
        {
            Thread.Sleep(100);

            if (i == 0)
            {
                Console.WriteLine("Value not changed and is {0}", i);
            }
            else
            {
                Console.WriteLine(" Value changed to {0}.", i);
            }
        });

        thread.Start();


        i = 1;
    }

此方法创建一个线程,该线程访问在方法中创建的局部变量。 到访问此变量时,方法已完成,因此不应存在局部变量i。但代码运行没有任何麻烦。根据我的理解,方法块结束后局部变量不存在。我无法得到它。

5 个答案:

答案 0 :(得分:15)

这是有效的,因为编译器会重写您的代码以使用closure

由于您在lambda中使用变量,变量最终会变为类的成员。编译后的代码不包含i 的局部变量 - 即使你是这样编写的。相反,它重写您的代码以使用包含Int32作为成员变量的编译器生成的类,而“本地代码”以及lambda则引用此类成员。

有关详细信息,请参阅此blog post on closures,它可让您大致了解编译器在此处执行的操作。

答案 1 :(得分:5)

  

根据我的理解,方法块结束后局部变量不存在。我无法得到它。

你的理解完全错了。局部变量的定义特征是其名称仅在其声明块中的范围内。这就是为什么它被称为“本地”变量 - 因为它的名称只能在本地显示

你认为“本地”的意思是“短暂的”。那是不对的;本地变量可以是短暂的,但有三种情况不可能:当它是匿名函数的封闭外部变量时,当它在迭代器块中时,或者当它在异步阻止。

巧合的是,你的问题是我上周博客的主题;有关详细信息,请参阅:

http://blogs.msdn.com/b/ericlippert/archive/2012/01/16/what-is-the-defining-characteristic-of-a-local-variable.aspx

答案 2 :(得分:4)

这称为closure 它将局部变量扩展到方法的生命周期之外。

答案 3 :(得分:0)

作为新线程启动的lambda表达式在i上创建closure;由于闭包关闭变量而不是值,因此线程引用与外部作用域相同的对象,并且即使在外部作用域结束后也能够访问其变量。

答案 4 :(得分:0)

C#编译器通常将C#代码转换为名为MSIL的中间“语言”,与C#一样,它具有局部变量。 MSIL中的局部变量的行为与您期望的C#变量的行为相同:它们在定义它们的例程退出时不再存在。此外,当使用局部变量的C#代码被转换为使用局部变量的MSIL代码时,那些C#变量的行为类似于MSIL:它们在定义函数退出时不再存在。但是,并非所有使用局部变量的C#代码都使用MSIL局部变量来存储它们。

在各种情况下,编译器将获取编写的代码以使用局部变量,并在将其转换为MSIL之前,重写它以便它定义一个新的类对象,该对象包含有问题的变量的字段,然后重写访问到那些变量,所以他们改为访问新的字段。如果委托使用“变量”,则委托将被赋予对该新类对象的引用。即使定义了对象的函数退出,只要包括委托的任何副本在内的任何内容都包含引用,对象本身将继续存在。

确定编译器何时使用MSIL局部变量以及何时重写事物以使用类字段的规则可能很棘手,并且可以对例程的一部分进行小的更改以更改其他看似无关的部分的语义。 / p>