Lambda表达式作为ThreadStart的奇怪行为

时间:2012-10-27 23:14:03

标签: c# multithreading lambda

  

可能重复:
  C# - The foreach identifier and closures
  From Eric Lippert’s blog: “don’t close over the loop variable”

我使用lambda表达式作为ThreadStart参数,使用Thread类在新线程中运行方法。这是我的代码:

delegate void del();

static void Do(int i)
{
    Console.WriteLine(i);
}

static del CreateLoop(del Do)
{
    return () =>
    {
        while (true)
        {
            Do();
            Thread.Sleep(500);
        }
    };
}

static void Main(string[] args)
{
    int n = 0;
    var loop = CreateLoop(() => Do(n));
    new Thread(() => loop()).Start();
    Thread.Sleep(500);
    n = 1;
}

这是输出:

0
1
1
1
...

怎么可能?
为什么我更改整数变量n的值,也会更改iDo参数的值)?

2 个答案:

答案 0 :(得分:0)

您应该从中创建一个不同的变量,因此不会更改原始值。

毕竟,你所做的只是调用同样的旧'函数',lambda表达式一遍又一遍地传递变量i,这确实会改变。这就像你在某处存储var i的初始值一样。

答案 1 :(得分:0)

var loop = CreateLoop(() => Do(n));

这一行只是创建一个新函数并将其赋值给变量。除其他外,此函数将值n传递给Do函数。但是它没有调用Do函数,它只是创建一个函数,在执行时会调用Do函数。

然后启动一个调用函数等的新线程,但是新线程仍在执行Do(n),并将n变量传递给Do。那部分没有改变 - 你创建了一个引用内存中某个特定位置的函数(由变量n表示)并继续在内存中引用该位置,即使你更改了存储在那里的值。

我相信以下内容会“修复”您的代码:

var loop = (int x) => () => CreateLoop(() => Do(x));
new Thread(loop(n)).Start();

这会将n的值传递给loop所代表的函数,但loop函数会在内存中创建一个新位置(由x表示),其中存储价值。内存中的这个新位置不会受到n的后续更改的影响。也就是说,您创建的函数不直接引用内存中n为指针的位置。