关于在线程中传递的局部变量
本文关键字:局部变量 线程 | 更新日期: 2023-09-27 18:18:43
我很难理解下面程序的意外输出:
class ThreadTest
{
static void Main()
{
for(int i = 0; i < 10; i++)
new Thread(() => Console.Write(i)).Start();
}
}
查询:不同的代码运行在不同的线程有单独的堆栈?如果是,那么变量应该保留它们的值,因为int是值类型?
每个线程都有自己的堆栈。您所面临的问题与堆栈无关。问题在于它为匿名委托生成代码的方式。使用像反射器这样的工具来理解它正在生成的代码。下面的命令将解决您的问题:
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实例化中的区别。当您捕获一个变量时,它会创建与迭代次数一样多的实例。如果您没有捕获一个变量,它将尝试在所有迭代中使用相同的实例。
希望以上解释能澄清你的疑问。
谢谢
是的,线程有自己的堆栈。但是这里还有一个变量捕获的问题。尝试将代码更改为:
class ThreadTest
{
static void Main()
{
for(int i = 0; i < 10; i++)
{
int j = i;
new Thread(() => Console.Write(j)).Start();
}
}
}
注意到输出的变化了吗?每个线程都是以对变量的引用而不是值来启动的。当我插入int j = i;
行时,我们破坏了变量捕获。您的意外输出与线程的关系不如与闭包的关系大。