2012-11-04 36 views
0

我遇到了一個小問題。我有兩個線程執行循環,每次都需要返回/發送一個數字到GUI的線程。爲此,我使用BackGroundWorkerReportProgress簡單的任務沒有凍結gui

讓我們說這樣的事情:

我有一個BackgroundWorker的執行(DoWork的)一個簡單的循環,從0計數到什麼。循環的每個條目都使用ReportProgress事件將計數器發送到GUI的線程,該線程將打印計數器的值。

void worker_DoWork(object sender, DoWorkEventArgs e) 
    { 
     int count = 0; 
     BackgroundWorker Worker = (BackgroundWorker)sender; 

     while (count < 10000000) 
     { 
      Worker.ReportProgress(count); 
      count++; 
     } 
    } 

    void worker_ProgressChanged(object sender, ProgressChangedEventArgs e) 
    { 
     txt.Text = e.ProgressPercentage.ToString(); 
    } 

現在,這個opretion凍結了GUI。

我知道ReportProgress在創建BackGroundWorker的線程上調用ProgressChange處理程序,所以我認爲循環執行速度如此之快,因此GUI線程無法按要求打印值。

如何在不凍結GUI的情況下執行類似的任務?

我聽說過Dispatcher,但我不確定它的用途。

+2

你的意思是GUI的權利?因爲GUID是完全不同的東西,據我所知:-)另外,你是否使用WinForms?或者我可以假設WPF? – Daneo

+0

'GUI' =圖形用戶界面; 'GUID' =全局唯一標識符...僅供參考;) –

回答

3

問題在於,每當有事情發生變化時,您都會調用reportProgress。只有當您需要報告進度時,您才應該調用它。請參閱MSDN http://msdn.microsoft.com/en-us/library/ka89zff4.aspx。 你的DoWork更改爲這樣的事情:

while (count < 10000000) 
    { 
     if ((count % 1000) == 0) 
      Worker.ReportProgress(count); 
     count++; 
    } 

這將每1000個處理的項目之後,調用ReportProgress,因此不要把不必要的負載,以您的GUI線程

+0

是的,我意識到這一點..但是這不是我的原始循環,我的原始循環做了比這更大的東西,需要返回一個值到GUID的線程。 – Matan

0

我能做些什麼來執行類似任務沒有凍結GUID?

使用調度員讓我假設你使用WPF,反正,這將是:

void worker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    int count = 0; 
    BackgroundWorker Worker = (BackgroundWorker)sender; 

    while (count < 10000000) 
    { 
     Worker.ReportProgress(count); 
     count++; 
    } 
} 

void worker_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    Dispatcher.BeginInvoke((Action)(() => { txt.Text = e.ProgressPercentage.ToString(); })); 
} 

調用Dispatcher.BeginInvoke使給定的行動實際上是在UI線程上執行,使得當然,除了UI線程訪問UI元素之外,不會引發異常。

此外,你可以試試這個,只是作爲一種替代by using a task.

+1

ProgressChanged事件處理程序在創建Background Worker的線程中調用。但即使它不是在UI線程中創建的,它也會引發異常,而不會阻止UI。 – Ondra

+0

這並沒有奏效,但我不明白你爲什麼選擇這樣做? – Matan

+0

@Matan我正在考慮線程安全性,調用調度程序使100%確定操作在UI線程上執行。如果你在循環中放入一個Thread.Sleep(500),你是否看到相應的UI更新? – Daneo

1

您的示例代碼試圖在比GUI更快的速度來更新GUI可以處理的更新通知消息,所以充斥GUI的Windows消息與gunge隊列並阻止它處理其他消息 - GUI凍結。

監控非GUI線程中高速率操作的進度是輪詢更好的解決方案時的少數幾次之一。使用Forms.Timer事件來讀取並顯示'currentProgress'值,可能由線程的方法返回。 500ms是一個合理的計時器值 - 人類用戶無法跟上編輯/文本框中不斷變化的整數值,其速度遠遠快於此值。'理想情況下',應該鎖定currentProgress值的讀/寫,可能是使用原子操作符,但是如果每500ms只讀一個int值,那麼如果'線程的「真實」功能意味着進度計數不太可能連續緩存在寄存器中。

+0

謝謝,這也是我的想法,所以最後一件事,創建backgroundWorker的線程是否可以讀取進度計數器? – Matan

+0

我的意思是,創建backgroundworker的線程可以訪問屬於backgroundworker線程的currentProgress值嗎? – Matan