C# 使用/不使用委托()启动线程

C# 使用/不使用委托()启动线程,c#,multithreading,delegates,C#,Multithreading,Delegates,以下两者之间的区别是什么: new Thread(new ThreadStart(SomeFunc)) 以及: 此代码在我的计算机上提供奇怪的输出: public class A { int Num; public A(int num) { Num = num; } public void DoObj(object obj) { Console.Write(Num); } public voi

以下两者之间的区别是什么:

new Thread(new ThreadStart(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
}
公共A类
{
int-Num;
公共A(整数)
{
Num=Num;
}
公共无效DoObj(对象obj)
{
控制台写入(Num);
}
公营部门
{
控制台写入(Num);
}
}
///////空中的main()
对于(int i=0;i<10;i++)
{
(新线程(新线程开始((新的A(i)).Do))).Start();//第1行
(新线程(新线程开始(委托(){(新的A(i)).Do();}))).Start();//第2行
(新线程(委托(){(新的A(i)).Do();})).Start();//第3行
}
如果只执行第1行,则输出类似于:

0231564789

这是正常的,但如果执行第2行或第3行,则输出为:

3 3 5 7 9 10

有一些多个数字和一个10,这是非常奇怪的循环从来没有运行与数字10。这背后的伎俩是什么


谢谢。

与代表一起,您正在捕获
i

不同之处在于,使用
new ThreadStart((new A(i)).Do))
,您将在
for
循环中创建
A
的新实例,并将
i
作为参数。这意味着此时,取
i
的值并发送给构造函数。因此,您发送的委托不是创建
A
,而是将
A
实例的
Do
方法的A委托发送给构造函数

但是,使用
delegate(){(新的A(i)).Do();})
(两者),您将向线程发送
i
的引用

然后线程需要一些时间来启动,同时,
for
循环继续。当代理中使用
i
时(即线程已启动),for的
循环已移动到
3
,这就是您看到的。第二个和第三个线程也是如此。三个线程已启动,但等待启动线程完成一些工作。然后创建的线程启动(线程1、2和3)并完成它们的工作。窗口返回到带有
for
循环的线程,并继续启动线程4和5

一些阅读材料:


为了回答第一点,
delegate(){SomeFunc();}
创建了一个调用
SomeFunc()
的函数,而不使用
delegate()
只是将
SomeFunc
函数直接用作
ThreadStart
方法


在您的第二个问题中,您将看到C#匿名函数的实现细节。对
i
的所有三个引用都引用相同的
i
,该值递增三倍。这三个函数之间有一个竞争条件,这意味着在运行已启动的线程之前,
i
可以递增几次。

“什么时候调用对象a的构造函数?”有助于回答这个问题

newthreadstart((新的A(i)).Do))

当执行这行代码时,调用构造函数,ThreadStart委托保留对新创建的对象a上的
.Do
函数的引用

在第2行和第3行中,您使用的是匿名委托(在C#2.0中介绍)

匿名委托的内容在被调用之前不会执行;在这种情况下,当线程被分配一个时间片来执行此操作时,由线程执行

变量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();
}
for(int i=0;i<10;i++)
{
int-icopy=i;
(新线程(新线程开始(委托(){(新A(icopy)).Do();}))).Start();
}

有关链接的详细信息,请参见Eric Lippert的文章;添加到答案中。+1很好的解释,昨天我自己在for循环中使用新的Task.Factory.StartNew调用时遇到了这个问题。它导致了一些微妙的错误。在大多数情况下,一切正常,但如果系统繁忙,则多个任务使用相同的数据。然后我必须自己复制索引变量。e、 g.内部温度=i;delegate(){(新的(临时的)).Do();}对吗?@bahadir-完全正确。在for循环中,在
新线程之前
,您按照描述创建变量的副本,问题就消失了。请阅读以下部分:,阅读Lambda表达式和捕获的变量。我不会将其称为实现细节。这是非常基本的。实现细节是用户不关心的事情。
delegate() { (new A(i)).Do(); })
for (int i = 0; i < 10; i++)
{
     int icopy = i;
     (new Thread(new ThreadStart(delegate() { (new A(icopy)).Do(); }))).Start();
}