在这个例子中,我试图通过值传递,但是传递了引用。
for (int i = 0; i < 10; i++)
{
Thread t = new Thread(() => new PhoneJobTest(i);
t.Start();
}
这可以这样解决:
for (int i = 0; i < 10; i++)
{
int jobNum = i;
Thread t = new Thread(() => new PhoneJobTest(jobNum);
t.Start();
}
这里发生了什么?为什么原始示例传递引用?
答案 0 :(得分:16)
嗯,这就是C#的工作方式。语句中的lambda表达式构造一个词法闭包,它存储对i
的单个引用,即使在循环结束后仍然存在。
为了解决这个问题,你可以做你做过的事情。
欢迎在网络上阅读有关此特定问题的更多信息;我的选择是Eric Lippert's discussion here.
答案 1 :(得分:16)
如果从范围的角度来看待发生的事情,这将更容易理解:
for (int i = 0; i < 10; i++)
{
Thread t = new Thread(() => new PhoneJobTest(i);
t.Start();
}
基本上翻译成非常接近的东西:
int i = 0;
while (i < 10)
{
Thread t = new Thread(() => new PhoneJobTest(i);
t.Start();
i++;
}
当你使用lambda表达式,它使用在lambda之外声明的变量(在你的情况下,i
)时,编译器会创建一个叫做闭包的东西 - 一个“包装”i变量的临时类up并将其提供给lambda生成的委托。
闭包构造在与变量(i)相同的层次上,所以在你的情况下:
int i = 0;
ClosureClass = new ClosureClass(ref i); // Defined here! (of course, not called this)
while (i < 10)
{
Thread t = new Thread(() => new PhoneJobTest(i);
t.Start();
i++;
}
因此,每个Thread都会定义相同的闭包。
当您重新设计循环以使用临时时,将在该级别生成闭包:
for (int i = 0; i < 10; i++)
{
int jobNum = i;
ClosureClass = new ClosureClass(ref jobNum); // Defined here!
Thread t = new Thread(() => new PhoneJobTest(jobNum);
t.Start();
}
现在,每个Thread都有自己的实例,一切正常。
答案 2 :(得分:3)
简短回答:关闭。这里给出的答案很长(在其他地方):Differing behavior when starting a thread: ParameterizedThreadStart vs. Anonymous Delegate. Why does it matter?
答案 3 :(得分:2)
答案 4 :(得分:1)
这是因为C#将参数传递给lambda的方式。它将变量访问包装在编译期间创建的类中,并将其作为字段公开给lambda主体。
答案 5 :(得分:0)
使用匿名委托或lambda表达式时,会创建closure,因此可以引用外部变量。创建闭包时,堆栈(值)变量将提升到堆。
避免这种情况的一种方法是使用ParameterizedThreadStart委托启动线程。 E.G:
static void Main()
{
for (int i = 0; i < 10; i++)
{
bool flag = false;
var parameterizedThread = new Thread(ParameterizedDisplayIt);
parameterizedThread.Start(flag);
flag = true;
}
Console.ReadKey();
}
private static void ParameterizedDisplayIt(object flag)
{
Console.WriteLine("Param:{0}", flag);
}
巧合的是,我昨天遇到了这个概念:Link