关于在Thread中传递的局部变量

时间:2011-12-26 03:58:01

标签: c# .net multithreading variables

我很难理解以下程序的意外输出:

class ThreadTest
{
     static void Main()
     {
          for(int i = 0; i < 10; i++)
               new Thread(() => Console.Write(i)).Start();
     }

}

查询: 在不同线程中运行的不同代码具有单独的堆栈?如果是,那么变量应该保留它们的值,因为int是值类型?

2 个答案:

答案 0 :(得分:3)

每个线程都有自己的堆栈。您面临的问题与堆栈无关。问题是它为您的匿名委托生成代码的方式。使用像refelector这样的工具来理解它生成的代码。以下将解决您的问题:

static void Main() 
        {
            for (int i = 0; i < 10; i++)
            {
                int capture = i;
                new Thread(() => Console.Write(capture)).Start();
            }
        } 

引擎盖

每当您在匿名委托中使用外部作用域的变量(在您的案例变量i中)时,编译器会生成一个新类,该类包含匿名函数以及它从外部作用域使用的数据。因此,在您的情况下,生成的类包含 - 一个函数和数据成员来捕获变量i的值。类定义类似于:

class SomeClass
{
    public int i { get; set; }

    public void Write()
    {
        Console.WriteLine(i);
    }
}

编译器按如下方式重写代码:

SomeClass someObj = new SomeClass();
for (int i = 0; i < 10; i++)
{
    someObj.i = i;
    new Thread(someObj.Write).Start();
}

因此问题 - 你正面临着。捕获变量时,编译器会执行以下操作:

for (int i = 0; i < 10; i++)
{
    SomeClass someObj = new SomeClass();
    someObj.i = i;
    new Thread(someObj.Write).Start();
}

注意SomeClass实例化的区别。捕获变量时,它会创建与迭代次数一样多的实例。如果您没有捕获变量,它会尝试对所有迭代使用相同的实例。

希望,上述解释将澄清您的疑问。

由于

答案 1 :(得分:2)

是的,线程有自己的堆栈。但是在这里你也有变量捕获的问题。尝试将代码更改为:

class ThreadTest
{
    static void Main()
    {
         for(int i = 0; i < 10; i++)
         {
              int j = i;
              new Thread(() => Console.Write(j)).Start();
         }
    }
} 

注意输出的变化?每个线程都是通过引用变量而不是值来启动的。当我插入int j = i;行时,我们正在打破变量捕获。您的意外输出与线程关系的关系不如关闭。