You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#控制台应用异步显示进度条被覆盖,求问题排查

异步进度条互相覆盖的问题分析与修复

问题根源

  1. 无专属坐标绑定:所有下载任务共享同一光标位置绘制进度条,没有为每个进度条分配独立的控制台显示区域。
  2. 绘制前未定位:DrawProgressBar仅保存当前光标左位置,未预先移动到每个进度条的专属坐标,导致绘制时直接覆盖其他任务的内容。
  3. 位置规划错误:Main中使用Console.WriteLine会自动换行偏移光标,且未为进度条预留独立行。
  4. 参数传递缺失: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 14:03:20