启动线程时的不同行为:ParameterizedThreadStart与Anonymous Delegate。 为什么这有关系?

当我运行下面的代码时,输​​出是“DelegateDisplayIt”,通常重复1-4次。 我已经运行了这个代码大概100次,并且没有一次输出曾经是“ParameterizedDisplayIt”。 因此,似乎创建并随后启动线程的方式会影响参数的传递方式。 使用匿名委托创建新线程时,该参数是对原始变量的引用,但是当使用ParameterizedThreadStart委托创建时,该参数是一个全新的对象? 我的假设是否正确? 如果是这样,这似乎是线程构造函数的一个奇怪的副作用,不是吗?

static void Main() { for (int i = 0; i < 10; i++) { bool flag = false; new Thread(delegate() { DelegateDisplayIt(flag); }).Start(); var parameterizedThread = new Thread(ParameterizedDisplayIt); parameterizedThread.Start(flag); flag = true; } Console.ReadKey(); } private static void DelegateDisplayIt(object flag) { if ((bool)flag) Console.WriteLine("DelegateDisplayIt"); } private static void ParameterizedDisplayIt(object flag) { if ((bool)flag) Console.WriteLine("ParameterizedDisplayIt"); } 

你的假设是正确的。 语句parameterizedThread.Start(flag)在调用时复制标志变量。

相比之下,匿名委托在闭包中捕获原始变量。 在委托执行DelegateDisplayIt方法之前,不会复制该变量。 此时,该值可能为true false,具体取决于原始线程在调用循环中的位置。

flag是一个布尔变量。 它通过值传递给委托。 它永远不会成立,因为伪值被复制并发送给委托。

如果使用匿名委托,则将隐式使用闭包来访问布尔值。

在.NET中,编译器构造一个匿名类型来保存作为闭包( 标志 )主题的变量的引用,然后匿名委托和方法将引用该变量。 然后他们将共享变量,因此如果更改变量,它们都会看到变化。

这真的不是一个线程问题,它关于传值和闭包。 这是关于闭包的一篇不错的博文 。 按值传递非常简单; 如果你需要了解我的建议,我建议你购买一份由Jeffrey Richter提供的CLR Via C#。

我们来看第一个案例:

 for (int i = 0; i < 10; i++) { bool flag = false; new Thread(delegate() { DelegateDisplayIt(flag); }).Start(); flag = true; } 

在构造匿名委托时,flag的值为false,但是当DelegateDisplayIt方法执行时,下一行已将该标志设置为true,并且您看到显示的输出。 这是另一个说明相同概念的例子:

 for (int i = 0; i < 5; i++) { ThreadPool.QueueUserWorkItem(state => Console.WriteLine(i)); } 

这将打印五次五次。

现在让我们来看第二种情况:

 for (int i = 0; i < 10; i++) { bool flag = false; var parameterizedThread = new Thread(ParameterizedDisplayIt); parameterizedThread.Start(flag); flag = true; } 

传递给回调的值是变量在调用Start方法时所拥有的值,这是false ,这就是你永远不会在控制台中看到输出的原因。