淺談C#多線程與UI響應

www.educity.cn     發佈者:shenywww                    來源:網絡轉載                     發佈日期:2014年10月06日                                     文章評論                   發表文章            html



 

  一.C#多線程概述編程

  1.後臺循環任務,少許UI更新:例如批量上傳文件,並提供進度。這種狀況使用BackgroundWorker組件是很是好的選擇。網絡

  2.耗時的後臺任務:這裏的耗時任務是指一個時間較長的任務,而且不能精確獲取進度,如:調用一個遠程WebService接口。這種狀況能夠開兩個線程,一個工做,一個更新UI(不能提供進度,只能顯示動畫表示系統在運行中)。www.educity.cn     發佈者:shenywww                    來源:網絡轉載                     發佈日期:2014年10月06日                                     文章評論                   發表文章            多線程



 

  一.C#多線程概述動畫

  1.後臺循環任務,少許UI更新:例如批量上傳文件,並提供進度。這種狀況使用BackgroundWorker組件是很是好的選擇。ui

  2.耗時的後臺任務:這裏的耗時任務是指一個時間較長的任務,而且不能精確獲取進度,如:調用一個遠程WebService接口。這種狀況能夠開兩個線程,一個工做,一個更新UI(不能提供進度,只能顯示動畫表示系統在運行中)。this

  3.耗時的UI任務:當工做壓力集中在UI響應上時,能夠在工做者線程中增長延時,從而讓UI線程得到響應時間。整個工做的整體時間會增長,但用戶響應效果會好不少。spa

  二.後臺的循環任務,少許UI更新線程

  這種狀況使用BackgroundWorker組件是最好的選擇。(詳見附一)orm

  三.後臺耗時任務

  在後臺執行一個不可分解的耗時任務,須要進行界面更新,以便讓客戶看上去程序有所響應。這種狀況下,UI線程通常也不知道工做線程什麼時候結束,因此通常執行循環任務,當工做線程結束後,關閉UI線程就能夠了。

     Threaduithread=null;     privatevoidbtnStart_Click(objectsender,EventArgse)     {     uithread=newThread(newThreadStart(this.UpdateProgressThread));     uithread.Start();     Threadworkthread=newThread(newThreadStart(this.DoSomething));     workthread.Start();     }     privatevoidDoSomething()     {     Thread.Sleep(5000);     uithread.Abort();     MessageBox.Show("workend");     }     privatevoidUpdateProgressThread()     {     for(inti=0;i<10000;i++)     {     Thread.Sleep(100);     this.Invoke(newAction(this.UpdateProgress),i);     }     }     privatevoidUpdateProgress(intv)     {     this.progressBar1.Value=v;     } 

  這裏只要注意一點:線程調用的方法都不能訪問用戶控件,必須經過委託調用Form的方法來實現界面更新。

  四.耗時的UI任務

  當整個工做壓力集中在UI響應上時,能夠在工做者線程中增長延時,從而讓UI線程得到響應時間。整個工做的整體時間會增長,但用戶響應效果會好不少。

     privatevoidFormInitForm_Load(objectsender,EventArgse)     {     this.listView1.Items.Clear();     Threadworkthread=newThread(newThreadStart(this.DoSomething));     workthread.Start();     }     privatevoidDoSomething()     {     for(inti=0;i<30;i++)     {     this.Invoke(newAction(this.LoadPicture),i);     Thread.Sleep(100);     }     }     privatevoidLoadPicture(inti)     {     stringstringtext=string.Format("Item{0}",i);     ListViewItemlvi=newListViewItem(text,0);     this.listView1.Items.Add(lvi);     Thread.Sleep(200);//模擬耗時UI任務,非循環,不可分解     } 

  五.補充

  1.Invoke和BeginInvoke

  在多線程編程中,咱們常常要在工做線程中去更新界面顯示,而在多線程中直接調用界面控件的方法是錯誤的作法,正確的作法是將工做線程中涉及更新界面的代碼封裝爲一個方法,經過Invoke或者BeginInvoke去調用,二者的區別就是一個致使工做線程等待,而另一個則不會。

  而所謂的「一面響應操做,一面添加節點」永遠只能是相對的,使UI線程的負擔不至於太大而以,由於界面的正確更新始終要經過UI線程去作,咱們要作的事情是在工做線程中包攬大部分的運算,而將對純粹的界面更新放到UI線程中去作,這樣也就達到了減輕UI線程負擔的目的了。

  2.Application.DoEvent

  在耗時的循環的UI更新的方法中,插入Application.DoEvent,會使界面得到響應,Application.DoEvent會調用消息處理程序。

     privatevoidbutton2_Click(objectsender,EventArgse)     {     for(inti=0;i<30;i++)     {     stringstringtext=string.Format("Item{0}",i);     ListViewItemlvi=newListViewItem(text,0);     this.listView1.Items.Add(lvi);     Thread.Sleep(200);     for(intj=0;j<10;j++)     {     Thread.Sleep(10);     Application.DoEvents();     }     }     }     3.Lock     lock(object)     {     }     等價與     try     {     Monitor.Enter(object);     }     finally     {     Monitor.Exit(object)     } 

  附一:

  BackgroundWorker組件使用說明

  一.概述

  BackgroundWorker是·NET2.0提供的一個多線程組件,在應用程序中使用,能夠很是簡單方便地實現UI控件通訊,並自動處理多線程衝突問題。

  二.基本屬性

  1.WorkerReportsProgress,bool:是否容許報告進度;

  2.WorkerSupportsCancellation,bool:是否容許取消線程。

  3.CancellationPending,bool,get:讀取用戶是否取消該線程。

  三.基本事件

  1.DoWork:工做者線程

  2.RunWorkerCompleted:線程進度報告

  3.ProgressChanged:線程結束報告

  四.基本方法

  1.RunWorkerAsync():啓動工做者線程;

  2.CancelAsync():取消工做者線程;

  3.ReportProgress(int);報告進度

  五.代碼

     //啓動     privatevoidbtnStart_Click(objectsender,EventArgse)     {     this.btnStart.Enabled=false;     this.btnStop.Enabled=true;     this.backgroundWorker.RunWorkerAsync();     }     //通知線程中止     privatevoidbtnStop_Click(objectsender,EventArgse)     {     this.backgroundWorker.CancelAsync();     }     //工做者線程     privatevoidbackgroundWorker_DoWork(objectsender,DoWorkEventArgse)     {     for(inti=0;i<150;i++)     {     if(backgroundWorker.CancellationPending)//查看用戶是否取消該線程     {     break;     }     System.Threading.Thread.Sleep(50);//乾點實際的事     backgroundWorker.ReportProgress(i);//報告進度     }     }         //線程進度報告     privatevoidbackgroundWorker_ProgressChanged(objectsender,ProgressChangedEventArgse)     {     this.progressBar1.Value=e.ProgressPercentage*100/150;     }     //線程結束報告     privatevoidbackgroundWorker_RunWorkerCompleted(objectsender,RunWorkerCompletedEventArgse)     {     this.btnStart.Enabled=true;     this.btnStop.Enabled=false;     } 

  3.耗時的UI任務:當工做壓力集中在UI響應上時,能夠在工做者線程中增長延時,從而讓UI線程得到響應時間。整個工做的整體時間會增長,但用戶響應效果會好不少。

  二.後臺的循環任務,少許UI更新

  這種狀況使用BackgroundWorker組件是最好的選擇。(詳見附一)

  三.後臺耗時任務

  在後臺執行一個不可分解的耗時任務,須要進行界面更新,以便讓客戶看上去程序有所響應。這種狀況下,UI線程通常也不知道工做線程什麼時候結束,因此通常執行循環任務,當工做線程結束後,關閉UI線程就能夠了。

     Threaduithread=null;     privatevoidbtnStart_Click(objectsender,EventArgse)     {     uithread=newThread(newThreadStart(this.UpdateProgressThread));     uithread.Start();     Threadworkthread=newThread(newThreadStart(this.DoSomething));     workthread.Start();     }     privatevoidDoSomething()     {     Thread.Sleep(5000);     uithread.Abort();     MessageBox.Show("workend");     }     privatevoidUpdateProgressThread()     {     for(inti=0;i<10000;i++)     {     Thread.Sleep(100);     this.Invoke(newAction(this.UpdateProgress),i);     }     }     privatevoidUpdateProgress(intv)     {     this.progressBar1.Value=v;     } 

  這裏只要注意一點:線程調用的方法都不能訪問用戶控件,必須經過委託調用Form的方法來實現界面更新。

  四.耗時的UI任務

  當整個工做壓力集中在UI響應上時,能夠在工做者線程中增長延時,從而讓UI線程得到響應時間。整個工做的整體時間會增長,但用戶響應效果會好不少。

     privatevoidFormInitForm_Load(objectsender,EventArgse)     {     this.listView1.Items.Clear();     Threadworkthread=newThread(newThreadStart(this.DoSomething));     workthread.Start();     }     privatevoidDoSomething()     {     for(inti=0;i<30;i++)     {     this.Invoke(newAction(this.LoadPicture),i);     Thread.Sleep(100);     }     }     privatevoidLoadPicture(inti)     {     stringstringtext=string.Format("Item{0}",i);     ListViewItemlvi=newListViewItem(text,0);     this.listView1.Items.Add(lvi);     Thread.Sleep(200);//模擬耗時UI任務,非循環,不可分解     } 

  五.補充

  1.Invoke和BeginInvoke

  在多線程編程中,咱們常常要在工做線程中去更新界面顯示,而在多線程中直接調用界面控件的方法是錯誤的作法,正確的作法是將工做線程中涉及更新界面的代碼封裝爲一個方法,經過Invoke或者BeginInvoke去調用,二者的區別就是一個致使工做線程等待,而另一個則不會。

  而所謂的「一面響應操做,一面添加節點」永遠只能是相對的,使UI線程的負擔不至於太大而以,由於界面的正確更新始終要經過UI線程去作,咱們要作的事情是在工做線程中包攬大部分的運算,而將對純粹的界面更新放到UI線程中去作,這樣也就達到了減輕UI線程負擔的目的了。

  2.Application.DoEvent

  在耗時的循環的UI更新的方法中,插入Application.DoEvent,會使界面得到響應,Application.DoEvent會調用消息處理程序。

     privatevoidbutton2_Click(objectsender,EventArgse)     {     for(inti=0;i<30;i++)     {     stringstringtext=string.Format("Item{0}",i);     ListViewItemlvi=newListViewItem(text,0);     this.listView1.Items.Add(lvi);     Thread.Sleep(200);     for(intj=0;j<10;j++)     {     Thread.Sleep(10);     Application.DoEvents();     }     }     }     3.Lock     lock(object)     {     }     等價與     try     {     Monitor.Enter(object);     }     finally     {     Monitor.Exit(object)     } 

  附一:

  BackgroundWorker組件使用說明

  一.概述

  BackgroundWorker是·NET2.0提供的一個多線程組件,在應用程序中使用,能夠很是簡單方便地實現UI控件通訊,並自動處理多線程衝突問題。

  二.基本屬性

  1.WorkerReportsProgress,bool:是否容許報告進度;

  2.WorkerSupportsCancellation,bool:是否容許取消線程。

  3.CancellationPending,bool,get:讀取用戶是否取消該線程。

  三.基本事件

  1.DoWork:工做者線程

  2.RunWorkerCompleted:線程進度報告

  3.ProgressChanged:線程結束報告

  四.基本方法

  1.RunWorkerAsync():啓動工做者線程;

  2.CancelAsync():取消工做者線程;

  3.ReportProgress(int);報告進度

  五.代碼

     //啓動     privatevoidbtnStart_Click(objectsender,EventArgse)     {     this.btnStart.Enabled=false;     this.btnStop.Enabled=true;     this.backgroundWorker.RunWorkerAsync();     }     //通知線程中止     privatevoidbtnStop_Click(objectsender,EventArgse)     {     this.backgroundWorker.CancelAsync();     }     //工做者線程     privatevoidbackgroundWorker_DoWork(objectsender,DoWorkEventArgse)     {     for(inti=0;i<150;i++)     {     if(backgroundWorker.CancellationPending)//查看用戶是否取消該線程     {     break;     }     System.Threading.Thread.Sleep(50);//乾點實際的事     backgroundWorker.ReportProgress(i);//報告進度     }     }         //線程進度報告     privatevoidbackgroundWorker_ProgressChanged(objectsender,ProgressChangedEventArgse)     {     this.progressBar1.Value=e.ProgressPercentage*100/150;     }     //線程結束報告     privatevoidbackgroundWorker_RunWorkerCompleted(objectsender,RunWorkerCompletedEventArgse)     {     this.btnStart.Enabled=true;     this.btnStop.Enabled=false;     } 
相關文章
相關標籤/搜索