【问题标题】:Captured variables in ParameterizedThreadStartParameterizedThreadStart 中捕获的变量
【发布时间】:2013-07-13 17:39:32
【问题描述】:

我有以下代码创建 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 的每个值都有自己的线程。谁能解释一下为什么?

【问题讨论】:

    标签: c# multithreading captured-variable


    【解决方案1】:

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

    这里的 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 而不是 threadNumber 来编写 lambda:

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

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

    【讨论】:

    • 感谢克里斯的详细解释,是按价值复制的部分让我感到困惑!
    • @Maverick 注意即使在他给出的“错误”情况下,您仍然可以通过在关闭之前添加一行 var i1 = i 并在您使用 i 的任何地方使用 i1 来获得正确的结果.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-21
    • 1970-01-01
    • 2011-09-15
    • 2013-11-26
    相关资源
    最近更新 更多