如何在多线程C#中使用基于文本的控制台进度条

时间:2016-08-08 13:08:04

标签: c# multithreading console progress-bar progress

您好我有一个程序可以并行下载+提取文件(在线程中)。 它是一个控制台应用程序,我想为每个线程中的每个操作显示进度条。 例如:

文件1 [========== 35%]下载100mb的35mb

文件2 [==== 20%] 20mb下载100mb

File1已下载,

文件1 [============= 50%]提取50%。 等等。

注意:我可以在下面的代码中显示控制台输出,但是想在我的控制台APP中使用此进度条。

在这种情况下,如何使用https://gist.github.com/DanielSWolf/0ab6a96899cc5377bf54中提出的解决方案?

public static void DownloadAndGetFiles()
    {
        try
        {
            Parallel.ForEach(FileIds, currentId =>

            { 
               int currentId = FileIds.Id 
               clientFileDownload(currentId);
            });
        }

        catch (Exception e)
        {

        }
    }

private static void clientFileDownload(int currentId)
{
    WebClient client = new WebClient();
    client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
    client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
    string downloadedFile = @"d:\tmp\";
    client.DownloadFileAsync(new Uri(currentId.URL), downloadedFile); //some URL
    while (client.IsBusy) { }
    string temp = ExtractAndRename(currentId);
}

private static void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
   //Prints: "Downloaded 3mb of 61.46mb  (4%)"
   Console.WriteLine("Downloaded "
              + ((e.BytesReceived / 1024f) / 1024f).ToString("#0.##") + "mb"
              + " of "                 
              + ((e.TotalBytesToReceive / 1024f) / 1024f).ToString("#0.##") + "mb"
              + "  (" + e.ProgressPercentage + "%)");
}

private static string ExtractAndRename(int currentId)
{
        //using SevenZipExtractor lib http://stackoverflow.com/questions/20898794/how-to-extract-files-in-my-archive-one-by-one-using-sevenzipsharp
        SevenZipExtractor extractor = new SevenZipExtractor(@"d:\tmp\" + id.Name);
        extractor.Extracting += extractor_Extracting;
        extractor.ExtractArchive(@"d:\tmp\" + extractName[0]);
        return (@"d:\tmp\" + extractName[0]);
}

public static void extractor_Extracting(object sender, SevenZip.ProgressEventArgs p)
{
              Console.ForegroundColor = ConsoleColor.Yellow;
              Console.Write("\b\b{0}% Extracted", p.PercentDone);
              Console.ResetColor();
}

1 个答案:

答案 0 :(得分:2)

  1. 为每个线程提供一个变量y,其中包含允许写入的行号。
  2. 在线程想要更​​新屏幕之前,请创建一个锁。控制台一次只能由一个线程使用。否则几个线程的结果会混淆。
  3. 将光标移动到y指定的行并更新该行。
  4. 解锁。
  5. 一个例子:

    static private readonly object _sync = new object();
    
    private static void UpdateProgress(int y, string item, int progress, int total)
    {
        int percentage = (int)100.0 * progress / total;
        lock(_sync)
        {
            Console.CursorLeft = 0;
            Console.CursorTop = y;
            Console.Write(item + " [" + new string('=', percentage / 2) + "] " + percentage + "%");
        }
    }
    

    您可以从方法clientFileDownload调用此方法,该方法必须稍作修改:

    private static void clientFileDownload(int currentId, int y)
    

    并且应该在创建这样的线程时调用:

    int y = 0;
    Parallel.ForEach(FileIds, currentId =>
    { 
        int currentId = FileIds.Id 
        clientFileDownload(currentId, y);
        Interlocked.Increment(ref y);
    });