使用/不使用委托()启动线程
有什么区别:
new Thread(new ThreadStart(SomeFunc))
和:
new Thread( delegate() { SomeFunc();} )
此代码在我的计算机上提供奇怪的输出:
public class A { int Num; public A(int num) { Num = num; } public void DoObj(object obj) { Console.Write(Num); } public void Do() { Console.Write(Num); } } /////// in void main() for (int i = 0; i < 10; i++) { (new Thread(new ThreadStart((new A(i)).Do))).Start(); // Line 1 (new Thread(new ThreadStart(delegate() { (new A(i)).Do(); }))).Start(); // Line 2 (new Thread(delegate() { (new A(i)).Do(); })).Start(); // Line 3 }
如果仅执行第1行,则输出类似于:
0 2 3 1 5 6 4 7 8 9
这是好的,但如果执行第2行或第3行,输出为:
3 3 3 5 5 7 7 9 9 10
有一些多个数字和一个10很奇怪,循环永远不会以数字10运行。这些背后的诀窍是什么?
谢谢。
代表,你正在捕捉i
。
区别在于使用new ThreadStart((new A(i)).Do))
,你在for
循环中创建一个新的A
实例,其中i
作为参数。 这意味着在那时, i
的值被采用并发送给构造函数。 因此,您发送的委托不是A
的创建,但您实际上是将A
实例的Do
方法的委托发送给构造函数。
但是,使用delegate() { (new A(i)).Do(); })
delegate() { (new A(i)).Do(); })
(两者都有),你正在向线程发送i
的引用。
然后线程需要一些时间来启动,同时for
循环继续。 当i
在委托中使用i
(即线程已经启动), for
循环已经移动到3
,这就是你所看到的。 第二和第三个线程也是如此。 启动三个线程但等待启动线程完成一些工作。 然后创建的线程启动(线程1,2和3)并且他们完成他们的工作。 Windows使用for
循环返回到线程,然后继续启动线程4和5。
一些阅读材料:
为了回答你的第一点, delegate() { SomeFunc();}
创建一个调用SomeFunc()
的函数,而不使用delegate()
只是直接使用SomeFunc
函数作为ThreadStart
方法。
在第二个问题中,您将遇到C#匿名函数的实现细节。 对i
所有三个引用都指向相同的 i
,其增加三次。 你在三个函数之间有一个竞争条件,这意味着i
可以在启动的线程运行之前多次递增。
‘对象A的构造函数何时被调用?’ 帮助回答这个问题。
new ThreadStart((new A(i)).Do))
执行此行代码时 – 调用构造函数,并由ThreadStart委托保留对新创建的对象A的.Do
函数的引用。
在第2行和第3行中,您使用的是匿名委托(在C#2.0中引入)。
delegate() { (new A(i)).Do(); })
在调用委托之前,不会执行匿名委托的内容; 在这种情况下由线程分配时间片来执行此操作。
变量i仅在for循环的开始处声明一次,并且委托内容具有对它的引用(委托将执行此操作) – 当代码执行时,它必须在执行时获取i的值。
这解释了值10.当循环执行完毕后,i的值为10。 如果其中一个线程在循环完成后执行,它将输出10。
为避免出现多个数字问题,您可以创建循环变量的本地副本。 代表将继续参考其自己的icopy版本;
for (int i = 0; i < 10; i++) { int icopy = i; (new Thread(new ThreadStart(delegate() { (new A(icopy)).Do(); }))).Start(); }