c# 控制檯console進度條

1 說明

筆者大多數的開發在 Linux 下,多處用到進度條的場景,但又無需用到圖形化界面,因此就想着弄個 console 下的進度條顯示。php

2 步驟

  1. 清行顯示java

    //清行處理操做
    int currentLineCursor = Console.CursorTop;//記錄當前光標位置
    Console.SetCursorPosition(0, Console.CursorTop);//將光標至於當前行的開始位置
    Console.Write(new string(' ', Console.WindowWidth));//用空格將當前行填滿,至關於清除當前行
    Console.SetCursorPosition(0, currentLineCursor);//將光標恢復至開始時的位置

    若是要清除上一行,只需在清行處理操做前將調整光標位置提早一行,即:Console.SetCursorPosition(0, Console.CursorTop - 1);。緊接着Console.WriteLine(/*something*/);,便可實如今控制檯末尾行重複輸出。python

  2. 多線程下的輸出顯示ubuntu

    多線程下最容易出現的問題就是一個線程的輸出覆蓋其餘線程的輸出,或者是一個線程緊跟着上一個線程輸出而沒有換行,這些狀況多會致使輸出的混亂。爲了,解決這樣的問題,特地實現一個專門的輸出類來進行輸出顯示:c#

    public class Consoler
     {
         private static string lastContext = "";//用於記錄上次寫的內容
         private static readonly object _lock = new object();//加鎖保證只有一個輸出
         public static void Write(string context)
         {
             lastContext = context;//記錄上次寫的內容
             lock (_lock)
             {
                 Console.Write(context);
             }
    
         }
         /// <summary>
         /// 覆寫
         /// </summary>
         /// <param name="context"></param>
         public static void OverWrite(string context = null)
         {
             lastContext = context;//記錄上次寫的內容
             var strLen = context?.Length ?? 0;
             //空白格的長度,考慮到內容可能超出一行的寬度,因此求餘。
             var blankLen = Console.WindowWidth - strLen % Console.WindowWidth - 1;
             var rowCount = strLen / Console.WindowWidth;
             Console.SetCursorPosition(0, Console.CursorTop - rowCount);
             //空白只需填充最後一行的剩餘位置便可。
             lock (_lock)
             {
                 Console.Write(context + new string(' ', blankLen));
             }
         }
    
         public static void WriteLine(string context = null)
         {
             ClearConsoleLine();//清除最後一行
             lock (_lock)
             {
                 Console.WriteLine(context);
                 if (!string.IsNullOrWhiteSpace(lastContext))
                     Console.Write(lastContext);//從新輸出最後一次的內容,不然有較明顯的閃爍
                 lastContext = null;
             }
         }
    
         public static void ClearConsoleLine(int invertedIndex = 0)
         {
             int currentLineCursor = Console.CursorTop;
             int top = Console.CursorTop - invertedIndex;
             top = top < 0 ? 0 : top;
             Console.SetCursorPosition(0, top);
             Console.Write(new string(' ', Console.WindowWidth - 1));
             Console.SetCursorPosition(0, currentLineCursor);
         }
     }

    實際測試時,使用多 Task (模擬多線程)去進行輸出實驗:多線程

    static void Main(string[] args)
     {
         Console.WriteLine("Hello World!");
    
         var task1 = Task.Run(() =>
         {
             int count = 0, w, lw, rw;
             float p = 0;
             while (true)
             {
                 w = (int)(Console.WindowWidth * 0.6);
                 count %= 75;
                 p = count++ / 74f;
                 lw = (int)(p * w);
                 rw = w - lw;
                 Consoler.OverWrite($"from task1, [{new string('#', lw) + new string(' ', rw)}]:{p:#.00%}");
                 Thread.Sleep(100);
             }
         });
         var task2 = Task.Run(() =>
         {
             while (true)
             {
                 Consoler.WriteLine($"from task2, now:{DateTime.Now}");
                 Thread.Sleep(5000);
             }
         });
    
         var task3 = Task.Run(() =>
         {
             var rd = new Random();
             while (true)
             {
                 Consoler.WriteLine($"from task3, {new string('+', (int)(rd.NextDouble() * Console.WindowWidth))}");
                 Thread.Sleep(rd.Next(5000));
             }
         });
         Task.WaitAll(task1);
     }

    最終輸出結果:dom

    from task2, now:6/5/19 8:10:24 PM
     from task3, +++++++++++++++++++++++++++++++++
     from task2, now:6/5/19 8:10:29 PM
     from task3, +++++++++++++++++++++++++
     from task3, ++++
     from task2, now:6/5/19 8:10:34 PM
     from task3, +++++++++++++++++++++++
     from task3, ++++++++++++
     from task3, ++++++
     from task2, now:6/5/19 8:10:44 PM
     from task1, [###########################                     ]:58.11%

    task1 用來進行進度條的輸出,task2 和 task3 進行隨機輸出。能夠看出,task1 永遠在最後一行進行進度更新,其餘輸出任然能夠正常進行。實現的效果和 ubuntu 下執行更新命令sudo apt-get update的輸出相似。異步

  3. 總結性能

    雖然該例子是在 c#下完成的,但在 python,c,java 中通用。爲了保證輸出的有序性,程序中加了鎖,影像了多線程的效率,可是對於界面顯示是足夠的。若是須要高性能,那麼考慮使用相似於隊列式的異步更新輸出顯示的方法。測試

相關文章
相關標籤/搜索