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已经被主线程更新到了后续的值:
- 如果第一个工作线程启动时,主线程已经完成了第一次循环的自增操作,
i的值变成2,此时该线程就会调用fn(2),而非预期的fn(1); - 当所有工作线程都启动完成时,主线程的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
相关产品推荐
相关产品推荐

