C# for循环初始化Task时参数传递错误及文件访问冲突问题
问题成因
这个问题的核心是C#中闭包对循环变量的捕获特性导致的:
- C# for循环中声明的迭代变量
i在整个循环的生命周期内是同一个变量实例,你在lambda表达式() => newMethodForThreads(i)中直接引用i时,捕获的是i的引用,而非当前迭代的i的具体值。 Task.Factory.StartNew创建的任务不会立刻执行,会被调度到线程池等待运行。等到任务实际执行、读取i的值时,for循环大概率已经推进到后续迭代甚至已经结束,此时i的值已经被更新为后续迭代值,或是循环结束后的x+1。
这就直接导致两个你观察到的异常现象:
- 多个任务拿到相同的
i值,同时往同一个Threadedfile{i}.txt写入,触发文件被其他线程占用的IO异常 - 部分迭代的
i值还没被任务读取就被覆盖,出现“迭代值被跳过”的情况
修复方案
只需要在循环内部声明一个局部临时变量,接收当前迭代的i值,再在lambda中引用这个局部变量即可。每次迭代都会生成独立的临时变量实例,闭包捕获的是对应迭代的独立变量,不会出现值被覆盖的问题:
public static List<Task> createTasks(int x) { List<Task> taskList = new List<Task>(); for (int i = 1; i <= x; i++) { int temp = i; taskList.Add(Task.Factory.StartNew(() => newMethodForThreads(temp))); } return taskList; } public static void newMethodForThreads(int i) { File.WriteAllLines($"C:\\Users\\my_username\\Desktop\\Shenanigans\\Threadedfile{i}.txt", list); Console.WriteLine($"File Threadedfile{i}.txt finished."); }
补充说明:从C# 5.0开始,foreach循环的迭代变量已经调整为每次迭代生成新实例,不会存在这个捕获问题,但for循环的规则至今没有变更,依然是全局共享同一个迭代变量实例。
内容的提问来源于stack exchange,提问作者Alessandro Rizzi
相关产品推荐
相关产品推荐

