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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 10:29:06