有什么区别:
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运行。这些背后的诀窍是什么?
感谢。
答案 0 :(得分:15)
通过代理人,您正在抓取i
。
与new ThreadStart((new A(i)).Do))
不同的是,您在A
循环中以for
为参数创建了i
的新实例。这意味着在那时,i
的值被采用并发送给构造函数。因此,您发送的委托不是A
的创建,但您实际上是将Do
实例的A
方法的委托发送给构造函数。
但是,对于delegate() { (new A(i)).Do(); })
(两者都有),您要向该帖子发送i
的引用。
然后线程需要一些时间来启动,同时,for
循环继续。当委托中使用i
时(即线程已经开始),for
循环已移至3
,这就是您所看到的。第二和第三个线程也是如此。启动三个线程,但等待启动线程完成一些工作。然后创建的线程启动(线程1,2和3)并且他们完成他们的工作。 Windows返回到带有for
循环的线程,然后继续启动线程4和5。
一些阅读材料:
答案 1 :(得分:4)
要回答您的第一点,delegate() { SomeFunc();}
会创建一个调用SomeFunc()
的函数,而不使用delegate()
只会直接使用SomeFunc
函数作为ThreadStart
方法
在第二个问题中,您将遇到C#匿名函数的实现细节。对i
的所有三个引用都引用了相同的 i
,它增加了三次。您在三个函数之间存在竞争条件,这意味着i
可以在启动的线程运行之前多次递增。
答案 2 :(得分:1)
'对象A的构造函数何时被调用?'有助于回答这个问题。
new ThreadStart((new A(i)).Do))
当执行这行代码时 - 调用构造函数,并由ThreadStart委托保留对新创建的对象A的.Do
函数的引用。
在第2行和第3行中,您使用的是匿名委托(在C#2.0中引入)。
delegate() { (new A(i)).Do(); })
在调用委托之前,不会执行匿名委托的内容;在这种情况下,线程在为其分配时间片时就是这样做的。
变量i仅在for循环开始时声明一次,并且委托内容具有对它的引用(委托将执行此操作) - 当代码执行时,它必须获取i时的值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();
}