使用/不使用delegate()启动线程

时间:2010-11-09 13:38:26

标签: c# multithreading delegates

有什么区别:

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运行。这些背后的诀窍是什么?

感谢。

3 个答案:

答案 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();
}