An Example of BackgroundWorker

其實這篇文章跟上一篇是有點關聯的…… 我為了解決 Memory Leak ,抱著「反正我本來就還有另一個問題要靠 BackgroundWorker 來解決,不如碰碰運氣」的心態,將耗時又耗記憶體的那段程式改寫為 BackgroundWorker ,結果真的有用耶~ 所以我沒有嘗試 GC.Collect() ,因為用這種有副作用的 function 會覺得不太漂亮 :p

BackgroundWorker 的效果跟 Thread 差不多,但更容易使用,方法如下:

  1. 把 BackgroundWorker 元件拉進來,取名為 BGWorker1
  2. 將 WorkerReportsProgress 設為 True ,讓 BackgroundWorker 回報進度。
  3. 雙擊 DoWork 事件,然後如下撰寫:

    private void BGWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        // 另外根據 sender 建立一個 BackgroundWorker
        BackgroundWorker bw = sender as BackgroundWorker;
    
        // 取出參數,我不知道怎麼改成能夠取多參數的版本… 此例中 YourFunction 的第二個參數是 int
        int arg = (int)e.Argument;
    
        // 開始執行你要的工作
        e.Result = YourFunction(bw, arg);
    
        // 處理使用者按下「取消」的動作
        if (bw.CancellationPending)
        {
            e.Cancel = true;
        }
    }
    
  4. 雙擊 RunWorkerCompleted 事件,然後如下撰寫:

    private void BGWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Cancelled)
        {
            // 使用者中止了執行
        }
        else if (e.Error != null)
        {
            // 執行中發生錯誤,可用 e.Error.Message 得到錯誤訊息
        }
        else
        {
            // 執行作業正常完成
        }
    }
    
  5. 雙擊 ProgressChanged 事件,然後如下撰寫:

    private void BGWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        // 用 e.ProgressPercentage 可以取得目前執行進度百分比(0-100)
    }
    

    比較實用的範例:

    private long begin_tick = 0L;
    
    private void BGWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        // 取得工作開始的時間,記得要在 RunWorkerCompleted 中將 begin_tick 設回 0L
        // C# 中有 function scope 的 static variable 嗎?
        if (begin_tick.Equals(0L))
        {
            begin_tick = DateTime.Now.Ticks;
        }
    
        // 計算已執行時間
        TimeSpan elapse_time = new TimeSpan(DateTime.Now.Ticks - begin_tick);
    
        // 預估完整執行所需時間
        long total_tick = elapse_time.Ticks * 100L;
        // 避免 Division by Zero…… 這有更方便的寫法嗎?
        if (!e.ProgressPercentage.Equals(0))
        {
            total_tick /= e.ProgressPercentage;
        }
    
        // 預估剩餘時間
        TimeSpan remain_time = new TimeSpan(total_tick - elapse_time.Ticks);
    
        // 預估完成時間
        DateTime finish_time = new DateTime(DateTime.Now.Ticks + remain_time.Ticks, DateTimeKind.Local);
    
        ProgressBar.Value = e.ProgressPercentage;
        StatusLabel.Text = String.Format("{0}% (經過時間:{1:00}:{2:00}:{3:00} / 預估剩餘:{4:00}:{5:00}:{6:00} / 預估完成:{7})", e.ProgressPercentage, elapse_time.Hours, elapse_time.Minutes, elapse_time.Seconds, remain_time.Hours, remain_time.Minutes, remain_time.Seconds, finish_time.ToString("yyyy-MM-dd HH:mm:ss"));
    }
    
  6. 至於 YourFunction 則類似這樣:

    private void YourFunction(BackgroundWorker bw, int progress)
    {
        if (!BGWorker1.CancellationPending)
        {
            // 執行你要執行的作業
    
            // 回報目前執行進度百分比(0-100),沒錯,你必須自己計算
            BGWorker1.ReportProgress(progress);
        }
    }
    

    傳入參數是陣列,使用 for 迴圈的範例:

    private void YourFunction(BackgroundWorker bw, string[] argument)
    {
        int count = argument.Length;
        for (int i = 0; i < count; i++) {
            if (!BGWorker1.CancellationPending)
            {
                using (SomeClass obj = new SomeClass(argument[i])) {
                    BGWorker1.ReportProgress(i * 100 / count);
                }
            }
        }
    }
    

這篇文章讓你覺得...

新奇
0%
溫馨
0%
誇張
0%
難過
0%
實用
0%
高興
0%
無聊
0%
生氣
0%
Leave your thoughts
  • You can use some HTML in your comment.
  • Your comment may not display immediately due to spam filtering. Please wait for moderation.