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