2011-05-23 38 views
2

爲什麼進度條更新在理論上阻塞了UI線程?阻塞的UI線程中的ProgressBar更新

在簡單的應用程序中,我有一個ProgressBar和一個標籤。我在UI線程中運行一個耗時的方法,它嘗試更新ProgressBar和標籤。這是而不是應該工作,因爲被阻塞的UI線程。但ProgressBar正在更新!

直到我在窗體上做了任何事情並且它凍結,ProgressBar更新(標籤沒有)。

這是爲什麼?

實施例的代碼(放按鈕,進度和一個標籤上的形式):

private void button1_Click(object sender, EventArgs e) 
{ 
    while (true) 
    { 
     progressBar1.Value += 1; 
     label1.Text += "1"; 
     Thread.Sleep(100); 
    } 
} 

進度正在更新,標籤不是。我的問題不是如何使標籤更新,但爲什麼ProgressBar 更新。我知道線程,DoEvents,異步/等待,這不是答案。

+1

爲什麼它不應該工作?你能否展示一些代碼? – 2011-05-23 10:19:49

回答

4

我覺得很難完全回答這個問題,沒有拆卸一些Windows,這對我來說太麻煩了。

但基本上,當您在WinForms ProgressBar控件上設置.Value時,它只不過是用消息1026(PBM_SETPOS)調用SendMessage,它告訴Windows進度條設置其位置。

我會得出結論,Windows進度條在響應於WM_PAINT而同步響應PBM_SETPOS 以及重繪本身。或者,也許它正在另一個線程上運行一個定時器,以便製作炫目的炫目動畫,並且可以在不等待繪製消息的情況下重新繪製控件。

無論哪種方式,它只是Windows內部,你所看到的 - 和WM_PAINT處理程序外畫的東西是不是一個不尋常的技術,儘管它不是做事情的教科書的方式。

實際上,查看文檔PBM_SETPOS(http://msdn.microsoft.com/en-us/library/bb760844(v=vs.85).aspx)它被記錄爲導致重繪 - 我想這是故意做的,以幫助懶惰/沒經驗的阻止進度條更新工作,沒有所有常見的麻煩正常。

+0

死 - 比你,聽起來像一個答案。 – 2011-05-23 11:30:35

1

有不同的方法來回答你的問題。讓我解釋。如果您在用戶需要等待的情況下執行冗長的任務,則無論您是否在UI線程上運行,都無關緊要。無論哪種方式,用戶都必須等待任務完成才能繼續,因此他們無法使用該應用程序。

但是,在任務運行時,您可能希望用戶與應用程序的不同部分進行交互的操作中,可以創建一個工作線程來執行上述任務。您需要了解主UI線程用於處理UI。繪製進度條是UI的一部分。 WinForms/.NET的設計架構是您可以通過BackgroundWorker類或自己的接線原始線程創建後臺線程。它的設計。

然而,這一切都將隨着C#5.0與async和await關鍵字以及Task對象的改變而急劇變化。你可以在channel9上搜索TechDays 11,或者訪問我的Facebook(發佈了幾篇文章)。

如果您覺得傾向於在UI線程上保留您的操作,可以調用您的任務中的Application.DoEvents()以保持Windows消息流動,或者您可以以正確的方式正確執行線程。它很容易使用Thread類,委託和調用進行連接,甚至更容易使用一個BackgroundWorker,它的設計非常獨特,目的是在另一個線程上執行任務並報告0/100%級數值。

+0

我有一個非常嚴格的政策,downvoting每個答案,建議使用Application.DoEvents,但因爲這是否是一個很好的答案,我試圖抵制這種誘惑... – 2011-05-23 10:40:34

+2

我不明白這是什麼與這個問題有關。 OP表示UI消息泵已停止(即沒有DoEvents)。他問爲什麼進度條仍然繪畫(即爲什麼看起來progresssbar正在捕獲WM_PAINT消息(這是不真實的))。 – 2011-05-23 10:47:38

+0

感謝您的回答。我非常瞭解如何創建其他線程,爲什麼不使用Application.DoEvents(),並且我知道新的異步/等待概念。我的問題是關於不同的東西 - 爲什麼ProgressBar不斷更新它應該被凍結的地方(因爲_intentional_ frozen UI)。它正在更新,直到我在表單上做了任何事情 - 然後整個表單停止響應。 – 2011-05-23 10:53:11