You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

线程Lambda方法中传递循环索引仅打印最后值的问题

解决Lambda线程中循环索引只打印最后值的问题

嘿,这问题我太熟了——这是C#闭包捕获变量时的经典陷阱!咱们一步步来拆解原因和解决办法。

问题原因

你遇到的核心问题是:当在循环里用Lambda表达式创建线程时,Lambda捕获的是循环变量的引用,而不是当前迭代的具体值。

因为线程启动需要一定时间,等线程真正执行Lambda里的代码时,循环大概率已经跑完了,此时循环变量已经变成了最后一个迭代的值(比如你的ThreadNum=3,最后i会变成3),所以所有线程都会读取这个最终值,导致打印结果全是最后一个索引。

两种解决方案

方案1:在循环内创建局部变量保存当前索引

每次迭代时,把当前循环变量的值赋值给一个局部变量,让Lambda捕获这个局部变量。因为每个迭代的局部变量都是独立的实例,不会被后续循环覆盖,这样每个线程拿到的都是对应迭代的正确值。

修正后的代码示例:

class Program
{
    public static EventWaitHandle handle = new EventWaitHandle(false, EventResetMode.AutoReset);
    public static EventWaitHandle autohandle = new EventWaitHandle(false, EventResetMode.AutoReset);
    static readonly int ThreadNum = 3;

    static void Main(string[] args)
    {
        object lk = new object();
        for (int i = 0; i < ThreadNum; i++)
        {
            // 关键:创建当前迭代的局部变量,保存i的当前值
            int currentIndex = i;
            new Thread(() =>
            {
                handle.WaitOne();
                Console.WriteLine($"当前索引:{currentIndex}");
                autohandle.Set();
            }).Start();
        }

        // 唤醒线程并等待执行完成
        for (int j = 0; j < ThreadNum; j++)
        {
            handle.Set();
            autohandle.WaitOne();
        }
    }
}

方案2:使用参数化的ThreadStart

把索引作为参数直接传递给线程方法,完全避免闭包捕获的问题。线程启动时会拿到参数的副本,自然就是当前迭代的正确值。

修正后的代码示例:

class Program
{
    public static EventWaitHandle handle = new EventWaitHandle(false, EventResetMode.AutoReset);
    public static EventWaitHandle autohandle = new EventWaitHandle(false, EventResetMode.AutoReset);
    static readonly int ThreadNum = 3;

    static void Main(string[] args)
    {
        object lk = new object();
        for (int i = 0; i < ThreadNum; i++)
        {
            // 把当前索引作为参数传递给线程方法
            new Thread(PrintIndex).Start(i);
        }

        // 唤醒线程并等待执行完成
        for (int j = 0; j < ThreadNum; j++)
        {
            handle.Set();
            autohandle.WaitOne();
        }
    }

    // 独立的线程方法,接收索引参数
    static void PrintIndex(object indexObj)
    {
        int index = (int)indexObj;
        handle.WaitOne();
        Console.WriteLine($"当前索引:{index}");
        autohandle.Set();
    }
}

额外提醒

如果是使用C# 5.0及以后的版本,foreach循环已经做了优化——每次迭代的变量都是独立的,不会出现这个问题,但for循环仍然存在闭包捕获引用的陷阱,所以要注意区分两种循环的差异。

内容的提问来源于stack exchange,提问作者Bercovici Adrian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:18:43