5

我有以下代码创建 10 个线程,这些线程依次将消息写入控制台:

for (int i = 0; i < 10; i++)
{
    {
        Thread thread = new Thread((threadNumber) =>
            {
                for (int j = 0; j < 10; j++)
                {
                    Thread.Sleep(200);
                    Console.WriteLine(string.Format("Thread: {0}, Line: {1}", threadNumber, j));
                }                           
            });
        thread.Start(i);
    }
}

我的理解是ParameterizedThreadStart需要一个对象,该对象的引用副本被发送到线程。如果是这种情况,因为我没有i在每个循环中制作本地副本,所有新线程都将指向相同的内存位置,这意味着某些线程号可能会“丢失”。虽然运行了这个(甚至针对大量线程/睡眠时间),但每个值i都有自己的线程。谁能解释为什么?

4

1 回答 1

4

在创建一个匿名函数的意义上,您没有应用任何延迟i或“捕获”的 wrap 。

这里的 lambda 函数没有引用i任何地方,它的状态是完全内部化/包含的,所以这里没有问题:

(threadNumber) =>
{
    for (int j = 0; j < 10; j++)
    {
        Thread.Sleep(200);
        Console.WriteLine(string.Format("Thread: {0}, Line: {1}", threadNumber, j));
    }                           
});

这里的Start调用:

thread.Start(i);

按值传递i(即复制它的值),因为它是一种“值类型”并且它没有被任何类型的匿名函数捕获。从这个意义上说,它像任何正常的方法一样传递struct给任何正常的方法(因为这正是正在发生的事情)。


i相反,如果您使用而不是您的 lambda 来编写threadNumber

{
    for (int j = 0; j < 10; j++)
    {
        Thread.Sleep(200);
        Console.WriteLine(string.Format("Thread: {0}, Line: {1}", i, j));
    }                           
});

那你就麻烦了。在这种情况下i,指的是原始变量位置,每当线程执行时都会对其进行评估。这意味着它可能是创建时的当前值i(不太可能只是由于处理时间),或者是稍后在for循环中设置的值,或者最后一个可能的值10,并且很可能在迭代之间跳过或共享数字。

于 2013-07-13T17:50:02.027 回答