背景
我创建了一个“比赛”线程的简单程序。这还不完整,但它是学习新事物的试验台。
现在,我正在“学习如何创建线程”阶段。
程序如下:
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10, i++)
{
Thread racer = new Thread(() => GetRacer(i));
racer.Start();
{
Console.ReadLine();
}
public static Racer GetRacer(int designation)
{
return new Racer(Convert.ToString(designation));
}
}
public class Racer
{
public Racer(string name)
{
Random myRand = new Random(1234);
Thread.Sleep(myRand.Next(1000, 2000));
Console.WriteLine(name);
}
{
问题
我遇到的具体问题是多个Racers具有相同的名称,但i
的值在创建新Racer之前发生了变化。为什么会这样?
答案 0 :(得分:1)
答案在于来自@Sami的评论,您可以使用另一个本地变量来解决它。
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
int copy = i;
Thread racer = new Thread(() => GetRacer(copy));
racer.Start();
}
Console.ReadLine();
}
public static Racer GetRacer(int designation)
{
return new Racer(Convert.ToString(designation));
}
}
public class Racer
{
public Racer(string name)
{
Random myRand = new Random(1234);
Thread.Sleep(myRand.Next(1000, 2000));
Console.WriteLine(name);
}
}
在类似的帖子中可以找到更多解释。 Multithreading and closures in .NET
答案 1 :(得分:1)
在检查IL之后(这是我第一次这样做,我会承认),我发现问题在于Lambda的实现方式。作为免责声明,我不完全理解这里发生的事情,但我想我已经足够了解问题所在。
如果我们查看IL,我们可以看到编译器创建了一个名为<>c__displayclass0_0
的新对象。这个类就是实现lambda的方式。
它创建一个字段.field public int32 i
,它对应于lambda的输入,以及一个与lambda本身对应的方法。
Main
方法只有<>c__displayclass0_0
的一个实例。每当for循环更改i
的值时,它也会更新<>c__displayclass0_0.field public int32 i
的值。
然后创建并启动新线程,向其传递对<>c__displayclass0_0
的一个实例的引用。然后,因为for循环的迭代完成,它再次启动for循环。因此,如果<>c__displayclass0_0.field public int32 i
的值恰好在新线程获得执行时间之前发生更改,则将使用i
的新值。
这有一个有趣的副作用,有时一个线程会显示值10,因为for循环在检查之前会增加i,看它是否低于10。