C# 中的闭包 - 在具有值类型的函数调用中启动线程

本文关键字:类型 函数调用 线程 启动 闭包 | 更新日期: 2023-09-27 17:56:43

我有这段代码,它可以按照我想要的方式工作,但我不明白为什么。考虑到 C 中的堆栈,C++,我猜 p 变量将在每次调用时出现在堆栈上,然后在方法返回时擦除。线程的闭包如何捕获它,甚至每次捕获正确的值?输出是我想要的 - 文件是"_a"、"_b"、"_c"。

public enum enumTest
    {
        a = 1,
        b =2,
        c=3
    }     
 private void Form1_Load(object sender, EventArgs e)
    {
        callme(enumTest.a);
        callme(enumTest.b);
        callme(enumTest.c);
    }
    private void callme(enumTest p)
    {
        Thread t = new Thread(() =>
            {
                Thread.Sleep(2000);
                Guid guid = Guid.NewGuid();
                File.WriteAllText(guid.ToString() + "_" + p.ToString(), "");
            });
        t.Start();
    }

C# 中的闭包 - 在具有值类型的函数调用中启动线程

Lambda只是光荣的匿名代表

  • 里克·斯特拉尔〔http://www.west-wind.com/weblog/posts/2008/Apr/26/Variable-Scoping-in-Anonymous-Delegates-in-C〕

Rick 的文章描述了编译器如何生成处理enumTest p值和delegate的类。

还有很好的信息 匿名函数体变量保存在哪里?

基本上,编译器会创建一个"闭包类"的新实例,其中包含必须传递给 lambda 的局部变量。 这就是您输出正确的原因。

更新

在以下情况下:

for (int i=0; i<10; i++) 
{
    var t = new Thread(() => { Console.WriteLine(i); }); 
    t.Start(); 
}

变量iforlambda之间共享。 每个线程都在访问相同的i。 由于 for 循环倾向于在任何线程运行之前进行 finsih,因此您看到的只是"10"。

参见 http://msdn.microsoft.com/en-us/library/0yw3tz5k(v=vs.80).aspx

这与闭包无关,这里没有任何价值捕获。

这里发生的事情是,您的p参数被按值复制到线程的函数中。每次传递给函数时,都会将新值 p 复制到函数中。

线程的闭包如何捕获它,甚至每次捕获正确的值?

这就是编译器的魔力。仅仅因为 lambda 正在使用 p 参数,编译器会以不同的方式对待它。 p不是放在堆栈上,而是放在堆上。这就是为什么它在callme()终止后仍然存在的原因。