在ParameterizedThreadStart中捕获变量

Mav*_*ick 5 c# multithreading captured-variable

我有以下代码创建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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我的理解是ParameterizedThreadStart将一个引用副本的对象发送到该线程.如果是这种情况,因为我没有i在每个循环中创建一个本地副本,所有新线程将指向相同的内存位置,这意味着某些线程号可能被"遗漏".虽然运行了这个(甚至对更多的线程/休眠时间),每个值i都有自己的线程.有谁能解释为什么?

Chr*_*air 4

您还没有应用任何延迟或“捕获”的内容,即创建将包装的匿名函数i

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

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

这里的调用Start

thread.Start(i);
Run Code Online (Sandbox Code Playgroud)

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


如果您将 lambda 编写为这样,而i不是使用threadNumber

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

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