Parallel.For与局部变量问题:控制台应用中多文档进度更新异常排查
解决Parallel.For中控制台进度更新覆盖同一行的问题
你遇到的核心问题很典型:并行任务共享控制台资源时,动态获取的光标位置会因为其他任务的输出而失效,而且每个任务只在启动时取一次Console.CursorTop,自然会导致所有进度都挤在同一行。下面给你一套简单有效的解决方案:
核心思路
给每个并行任务分配固定的专属输出行,而不是动态依赖光标位置;同时对控制台操作加锁,避免多线程干扰导致输出混乱。
分步修改代码
1. 提前为每个并行任务分配固定行号
在启动Parallel.For前,先预留控制台的标题行,然后给每个迭代任务分配一个唯一的行号(比如从第3行开始,前两行放标题):
// 先初始化控制台布局,输出标题 Console.WriteLine("📄 文档生成进度监控"); Console.WriteLine("----------------------------------------"); // 并行循环:给每个任务分配固定行号 Parallel.For(0, generationFile.nbOfFile, optionsParallel, i => { string fileName = $"{tmpDirectoryPath}/{i + 1}_{guid}.pdf"; // 每个任务的专属行号 = 索引 + 2(跳过前两行标题) int taskLine = i + 2; AddPage(fileName, generationFile, i, taskLine); });
2. 修改AddPage方法,传入固定行号
去掉动态获取Console.CursorTop的逻辑,直接用分配好的行号:
private static void AddPage(string fileName, GenerationFile generationFile, int i, int taskLine) { for (int j = 0; j < generationFile.SizeMaxFile; j++) { // 你的页面添加逻辑(保持不变) Page page = Page.Create(outDoc, size); AddText(outDoc, page, font, 14, i, fileName, j, generationFile.SizeMaxFile); for (int k = 0; k < 292; k++) { AddImage(outDoc, page, 30, 30); } outDoc.Pages.Add(page); // 计算正确的进度百分比(j从0开始,所以要+1) double progressPercent = (j + 1) * 100.0 / generationFile.SizeMaxFile; // 传入固定行号更新进度 ConsoleManager.UpdateConsole(i, progressPercent, taskLine, generationFile); } }
3. 线程安全的控制台更新方法
给控制台操作加锁,确保光标定位和输出是原子操作,避免多线程互相打断导致的输出错位:
// 定义静态锁对象,用于同步控制台操作 private static readonly object _consoleLock = new object(); public static void UpdateConsole(int fileNumber, double progression, int lineNumber, GenerationFile generationFile) { lock (_consoleLock) { Console.ForegroundColor = ConsoleColor.White; Console.SetCursorPosition(0, lineNumber); // 用PadRight清空该行残留的旧内容,避免显示混乱 string progressText = $"Fichier n°{fileNumber + 1}/{generationFile.nbOfFile} en cours de création : {progression:F1}% "; Console.WriteLine(progressText.PadRight(Console.WindowWidth)); } }
关键细节解释
- 固定行号:每个并行任务从一开始就拿到专属输出行,不会因为其他任务的输出改变光标位置而导致覆盖。如果你的
nbOfFile超过控制台可显示行数,可以考虑限制显示数量或者自动滚动,但小数量场景下固定行号最直观。 - 线程锁:
Console.WriteLine本身是线程安全的,但SetCursorPosition和WriteLine组合操作时,可能被其他线程打断,加锁后能保证每次更新是完整的原子操作。 - 进度计算修正:你之前的进度计算逻辑有问题(
progression = (progression / 100) * generationFile.ArchiveESC),现在改成当前完成页数除以总页数再乘100,得到正确的百分比,用F1格式化保留一位小数更美观。
内容的提问来源于stack exchange,提问作者user11931685
相关产品推荐
相关产品推荐

