解決c# progressBar更新出現界面假死


最近一個項目需求中的一個功能是需要用progressBar反映處理文件的進度。

研究了Invoke和BeginInvoke方法。

Control.Invoke 方法 (Delegate) :擁有此控件的基礎窗口句柄的線程上執行指定的委托。

Control.BeginInvoke 方法 (Delegate) :在創建控件的基礎句柄所在線程上異步執行指定委托。

我開始的想法是開一個線程處理,代碼如下:

         private delegate void DoDataDelegate(object number);
        private void button2_Click(object sender, EventArgs e)
        {
           // Thread myThread = new Thread(DoData);
            Thread myThread = new Thread(new ParameterizedThreadStart(DoData));
            myThread.IsBackground = true;
            myThread.Start(int.Parse(textBox1.Text)); //線程開始  
            Console.WriteLine("主線程繼續執行---------------");
        }
        private void DoData(object number)
        {
            if (progressBar1.InvokeRequired)
            {
                Console.WriteLine("開始");
                DoDataDelegate d = DoData;
                progressBar1.BeginInvoke(d, number);
                //代碼段D
            }
            else
            {
                progressBar1.Maximum = (int)number;
                Console.WriteLine("准備進行循環調用");
                for (int i = 0; i < (int)number; i++)
                {
                  //這里是一段耗時長的代碼
                  progressBar1.Value = i + 1;
                }
               
            }
        }                

 在上述代碼中當執行到progressBar1.BeginInvoke(d, number);時,myThread封送消息給UI,然后自己繼續執行代碼段D,代碼段D的執行相對於myThread是異步的。如果將

progressBar1.BeginInvoke(d, number);換成progressBar1.Invoke(d, number);其余的代碼不變,那么代碼段D將會等到線程myThread結束后才會執行。無論是調用BeginInvoke或者Invoke,因為調用者是progressBar1,所以更新progressBar1.Value時,是在"擁有此控件的基礎窗口句柄的線程(UI線程)執行指定的委托",在更新value值之前有一段耗時長的代碼,所以此時UI線程會阻塞,處於假死狀態。

我的解決辦法是使用BackgroundWorker 類。

 

            backgroundWorker1.WorkerReportsProgress = true;
            backgroundWorker1.DoWork += DoWork_Handler;
            backgroundWorker1.ProgressChanged += ProgressChanged_Handler;
         private void button2_Click(object sender, EventArgs e)
        {
            backgroundWorker1.RunWorkerAsync();
        }    
         private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {   
             for (int i = 1; i <= 100; i++)
            {
               //這里省略一段執行耗時的操作 
backgroundWorker1.ReportProgress(i); } } private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { // Change the value of the ProgressBar to the BackgroundWorker progress. progressBar1.Value = e.ProgressPercentage; }

 

 BackgroundWorker.RunWorkerAsync是創建了一個線程處理耗時的操作,backgroundWorker1_DoWork方法執行,執行到backgroundWorker1.ReportProgress(i);會觸發backgroundWorker1_ProgressChanged方法的執行,在backgroundWorker1_ProgressChanged方法中,更新value的值。這樣做UI界面和線程處理好使的工作是異步的,所以不會造成UI界面卡死的現象。

我了解了Invoke 和BeginInvoke的原理,但不知道怎么用他們實現更新ProgressBar的值,並保證UI界面不出現假死。所以我選擇了BackgroundWorker解決了我的問題。

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

再次更新。

經過和老師的討論,我自己進行了實踐。如果用ProgressBar反映處理耗時的程序的進度。可以開一個子線程處理耗時的部分,然后設置一個全局變量標識耗時的程序處理的進度。

代碼段簡化如下:

 

int  num=0;
int number=16;
private void button2_Click(object sender, EventArgs e)
        {
            progressBar1.Maximum = number;
           // Thread myThread = new Thread(DoData);
            Thread myThread = new Thread(new ParameterizedThreadStart(DoData));
            myThread.IsBackground = true;
            myThread.Start(number); //線程開始  
 
        }
        private void DoData(object number)
        {
                Console.WriteLine("准備進行循環調用"); 
                for (int i = 0; i < (int)number; i++)
                {
                  //這里是一段耗時長的代碼

                    num = i + 1;
                }
               
            }
        }          

 

 然后加入一個Timer每隔一定的時間訪問num的值。在Timer_tick函數中更新progressBar的value的值。

補充:在C#中的子線程中不能直接更新UI中的控件值。可以通過

1.Invoke或者BeginInvoke的方式調用委托的方法

2.使用BackGroundWorker類取代Thread類進行異步操作

3.通過設置窗體屬性,取消線程安全檢查來“避免跨線程操作異常”。(非線程安全,不建議使用)

4.通過UI線程的SynchronizationContext的Post/Send方法更新。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM