Task List未达预期输出,如何修复循环中Task值异常问题?
问题分析与修复方案
你遇到的这个问题,是C#里闭包捕获循环变量的经典坑!我来给你拆解原因和解决办法:
问题根源
循环中的变量i是一个共享变量,所有Task在创建时并没有立即执行,而是在后续异步调度时才会读取i的值。当Task真正开始运行时,循环大概率已经执行完毕,i已经变成了100,所以所有Task都会读取到这个最终值,导致j和cb里的内容全是100。
而逐行调试时,因为你手动控制了循环节奏,每个Task会在当前迭代时就被执行,此时i还是当前迭代的数值,所以结果正确。
修复方案
这里有两种可靠的解决办法,任选其一即可:
方案1:循环内创建局部变量捕获当前值
在每次循环迭代里,把i的值赋值给一个局部变量,让每个Task捕获这个独立的局部变量:
public static void TaskList() { ConcurrentBag<int> cb = new ConcurrentBag<int>(); List<Task> taskArray = new List<Task>(); for (int i = 0; i < 100; i++) { // 关键:用局部变量保存当前迭代的i值 int currentI = i; taskArray.Add(Task.Factory.StartNew((Object obj) => { int j = currentI; cb.Add(j); Debug.WriteLine("Task #{0} created on {1}", j, Thread.CurrentThread.ManagedThreadId); })); } // 必须等待所有任务执行完成,否则程序可能提前退出导致任务未执行 Task.WaitAll(taskArray.ToArray()); }
方案2:通过StartNew的参数传递值
利用Task.Factory.StartNew的第二个参数传递当前的i,因为参数是值传递,每个Task会拿到i的独立副本:
public static void TaskList() { ConcurrentBag<int> cb = new ConcurrentBag<int>(); List<Task> taskArray = new List<Task>(); for (int i = 0; i < 100; i++) { // 将i作为参数传递,值传递会复制当前i的状态 taskArray.Add(Task.Factory.StartNew((Object obj) => { int j = (int)obj; cb.Add(j); Debug.WriteLine("Task #{0} created on {1}", j, Thread.CurrentThread.ManagedThreadId); }, i)); } // 等待所有任务完成 Task.WaitAll(taskArray.ToArray()); }
额外提醒
原代码里缺少了等待所有Task完成的逻辑,即使修复了闭包问题,也可能因为程序提前退出,导致部分Task还没执行就被终止,所以一定要加上Task.WaitAll来确保所有任务都执行完毕。
内容的提问来源于stack exchange,提问作者paparazzo
相关产品推荐
相关产品推荐

