C#控制台应用异步显示进度条被覆盖,求问题排查
异步进度条互相覆盖的问题分析与修复
问题根源
- 无专属坐标绑定:所有下载任务共享同一光标位置绘制进度条,没有为每个进度条分配独立的控制台显示区域。
- 绘制前未定位:
DrawProgressBar仅保存当前光标左位置,未预先移动到每个进度条的专属坐标,导致绘制时直接覆盖其他任务的内容。 - 位置规划错误:
Main中使用Console.WriteLine会自动换行偏移光标,且未为进度条预留独立行。 - 参数传递缺失:
Download方法未接收进度条的位置参数,无法告知DrawProgressBar正确的绘制位置。
修复后的代码
public class test { private static readonly object _lock = new object(); // 添加left和top参数,指定进度条的绘制位置 public static void DrawProgressBar(int complete, int maxVal, int barSize, char progressCharacter, int left, int top) { lock (_lock) { Console.CursorVisible = false; // 先定位到指定坐标 Console.SetCursorPosition(left, top); decimal perc = (decimal)complete / maxVal; int chars = (int)Math.Floor(perc * barSize); // 优化字符串拼接方式 var p1 = new string(progressCharacter, chars); var p2 = new string(progressCharacter, barSize - chars); Console.ForegroundColor = ConsoleColor.Green; Console.Write(p1); Console.ForegroundColor = ConsoleColor.DarkGreen; Console.Write(p2); Console.ResetColor(); Console.Write(" {0}%", (perc * 100).ToString("N2")); } } public static async Task Main() { string[] files = new string[] { "file1.txt", "file2.txt", "file3.txt" }; var tasks = new List<Task>(); for (int i = 0; i < files.Length; i++) { int fileIndex = i; // 捕获循环变量,避免闭包问题 // 文件信息显示在2*fileIndex行 int infoLine = 2 * fileIndex; // 进度条显示在文件信息的下一行 int progressLine = infoLine + 1; int progressLeft = 1; Console.SetCursorPosition(progressLeft, infoLine); Console.WriteLine("Processing file {0} of {1}: {2}", fileIndex + 1, files.Length, files[fileIndex]); // 传递进度条的位置参数给Download tasks.Add(Task.Run(async () => await Download(progressLeft, progressLine))); } await Task.WhenAll(tasks); // 所有任务完成后,光标移到最后一行 Console.SetCursorPosition(0, 2 * files.Length); Console.WriteLine("\nAll files processed!"); Console.CursorVisible = true; } // 接收进度条的位置参数 private static async Task Download(int progressLeft, int progressLine) { for (int j = 0; j <= 100; j++) { DrawProgressBar(j, 100, 50, '=', progressLeft, progressLine); await Task.Delay(100); // 用Task.Delay替代Thread.Sleep,避免阻塞线程 } } }
关键修改点
- 绑定专属坐标:为每个进度条分配独立的行(文件信息行的下一行),确保各个进度条区域不重叠。
- 绘制前定位:
DrawProgressBar先通过Console.SetCursorPosition定位到指定坐标,再执行绘制操作。 - 修复闭包问题:循环中捕获
fileIndex变量,避免所有任务共享同一个循环变量值。 - 优化异步操作:用
Task.Delay替代Thread.Sleep,避免阻塞线程池线程;修正Task.Run中的异步调用,确保任务正确等待。 - 优化字符串拼接:用
new string(char, count)替代循环拼接,提升代码执行效率。
内容的提问来源于stack exchange,提问作者sandy
相关产品推荐
相关产品推荐

