在控制台应用程序中更新多行

Updating multiple line in a console application

我一直在与 Parallel.For 和局部变量作斗争。我正在尝试通过并行更新控制台应用程序中的百分比。我在多个文档中添加页面,我想更新每个文档的百分比。

这是我到目前为止的尝试:

我正在使用 Console.CursorTop 获取行号,我想将其传递给将覆盖该行的方法。

循环自 Program.cs

Parallel.For(0, generationFile.nbOfFile, optionsParallel,
    i =>
    {                    
        string fileName = $"{tmpDirectoryPath}/{i + 1}_{guid}.pdf";
        AddPage(fileName, generationFile, i);
    });

AddPage方法

private static void AddPage(string fileName, GenerationFile generationFile, int i)
{
    var cursorPosition = Console.CursorTop;

    //Ajout des pages
    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);
        ConsoleManager.UpdateConsole(i, j, cursorPosition, generationFile);
    }
}  

UpdateConsole方法

public static void UpdateConsole(int fileNumber, double progression, int cursorPosition, GenerationFile generationFile)
{
    progression = (progression / 100) * generationFile.ArchiveESC; 
    Console.ForegroundColor = ConsoleColor.White;
    Console.SetCursorPosition(0, cursorPosition);
    Console.WriteLine($"\rFichier n°{fileNumber + 1}/{generationFile.SizeMaxFile} en cours de création : {progression}%       ", Console.ForegroundColor);
}

我认为一切正常,除了 cursorPosition 一开始就取一个值并且永远不会改变,所以更新了同一行。我知道这与本地 and/or 共享变量有关,但我在并行处理方面相当新,所以即使有关于这个主题和 MSDN 的其他线程,我也不知道该怎么做。

我更喜欢处理进度报告的方式是让所有工作线程向共享进度字段报告,并有一个单独的计时器读取此字段并向用户报告进度。这让我可以控制报告进度的频率,而不管项目的处理速度有多快。我还想要一个允许不同方式报告进度的抽象层。毕竟,可以从控制台使用相同的方法,UI,或者根本不使用。

例如这样的事情:

public interface IMyProgress
{
    void Increment(int incrementValue);
}

public sealed class MyProgress : IMyProgress, IDisposable
{
    private readonly int totalItems;
    private readonly Timer myTimer;
    private volatile int progress;
    private int lastUpdatedProgress;
    public MyProgress(TimeSpan progressFrequency, int totalItems)
    {
        this.totalItems = totalItems;
        myTimer = new Timer(progressFrequency.TotalMilliseconds);
        myTimer.Elapsed += OnElapsed;
        myTimer.Start();
    }

    private void OnElapsed(object sender, ElapsedEventArgs e)
    {
        var currentProgress = progress;
        if (lastUpdatedProgress != currentProgress)
        {
            lastUpdatedProgress = currentProgress;
            var percent = currentProgress * 100 / (double)totalItems;
            Console.Write($"\rWork progress: {percent}%");
        }
    }
    public void Increment(int incrementValue) => Interlocked.Add(ref progress, incrementValue);
    public void Dispose() => myTimer?.Dispose();
}

这可以从类似以下的并行方法调用:

static void Main(string[] args)
{
    Console.WriteLine("Hello World!");

    var myWorkItems = Enumerable.Range(1, 10000).ToList();
    using var progress = new MyProgress(TimeSpan.FromSeconds(1), myWorkItems.Count);
    DoProcessing(myWorkItems, progress);
}

private static void DoProcessing(IEnumerable<int> items, IMyProgress progress)
{
    Parallel.ForEach(items, item =>
    {
        Thread.Sleep(20);
        progress.Increment(1);
    });
}

我在使用回车returns时会有点小心。根据我的经验,控制台应用程序往往像人类一样被其他程序使用,然后输出很可能会被重定向到一个文件,并且不支持回车 returns。所以我会尽量让输出看起来不错。

我会避免尝试移动光标。我试过了,结果不尽如人意,YMMV。