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

C#多线程实现出现意外输出的原因咨询

多线程闭包捕获变量导致的意外输出问题

问题代码

static void Main(string[] args)
{
    for (int i = 1; i <= 5; i++)
    {
        System.Threading.Thread workerThread = new System.Threading.Thread(() => fn(i));
        workerThread.Start();
    }
    System.Console.WriteLine("!End the main process!");
}
public static void fn(int x)
{
    System.Console.WriteLine($"fn method called for {x}");
    System.Threading.Thread.Sleep(5000);
    for (int i = 1; i <=2 ; i++)
    {
        System.Console.Write($" {x} ");
    }
}

预期输出

  • !End the main process!
  • fn method called for 1
  • fn method called for 2
  • fn method called for 3
  • fn method called for 4
  • fn method called for 5
  • 1 1 2 2 3 3 4 4 5 5

实际异常现象

  • 某次运行出现两次fn(2)调用,未触发fn(1)
  • 某次运行出现fn(6)调用

原因分析

这是闭包对循环变量的延迟绑定导致的典型问题:

lambda表达式() => fn(i)捕获的是循环变量i的引用,而非每次循环时i的当前值。主线程的for循环执行速度远快于工作线程的启动和执行速度,当工作线程真正开始执行lambda时,循环变量i已经被主线程更新到了后续的值:

  1. 如果第一个工作线程启动时,主线程已经完成了第一次循环的自增操作,i的值变成2,此时该线程就会调用fn(2),而非预期的fn(1);
  2. 当所有工作线程都启动完成时,主线程的for循环已经结束,此时i的值会自增到6(因为循环条件是i<=5,最后一次循环后i自增到6才会退出循环),如果有工作线程此时才开始执行lambda,就会传入6,出现fn(6)的情况。

解决办法

在每次循环内部创建一个局部变量,保存当前i的值,让lambda捕获这个局部变量而非循环变量i。这样每个工作线程都会绑定到独立的局部变量,其值为循环时i的当前快照:

static void Main(string[] args)
{
    for (int i = 1; i <= 5; i++)
    {
        int current = i; // 保存当前循环的i值到局部变量
        System.Threading.Thread workerThread = new System.Threading.Thread(() => fn(current));
        workerThread.Start();
    }
    System.Console.WriteLine("!End the main process!");
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 15:15:40