2010-12-02 87 views
1

我對如何在多線程應用程序中使用GUI有點困惑。線程,事件和GUI

我聽說有一個叫做UI線程的東西。我認爲這是我在應用程序啓動時的主要執行線程。

我也聽到(雖然我不是100%)在其他(非UI)線程上做UI的東西是一個非常糟糕的主意。

所以,如果我創建一個單獨的線程,並且我想在其中調用MyForm myForm = new MyForm(); myForm.ShowDialog();,那麼需要做出什麼更改才能使其「安全」?

此外,我有一些人告訴我,事件是在不同的線程上分解出來的。 (雖然我不確定我是否相信這一點。)如果他們是,那麼我很困惑。我可以打開一個對話框(即在事件myForm.ShowDialog()並沒有什麼真正可怕的情況發生。(也許這取決於如果事件委託被稱爲用InvokeBeginInvoke?)

+0

感謝您的所有偉大的答案!我希望我可以選擇多個作爲「正確」的答案。 – Vaccano 2010-12-02 19:33:13

回答

3

這裏是信息,可以幫助你走出的幾個位。你在非UI線程上使用UI所說的話不只是一個壞主意,你會得到一個異常。意思是,如果你在主線程中創建一個Form,然後產生一個後臺線程來做一些處理,然後想在後臺線程中更新表單,它會拋出一個異常。但在你的例子中,在後臺線程中創建表單的地方,你應該沒問題。不會被炸掉只要您只是觸摸相同的用戶界面

至於事件,事件處理程序在它們被引發的同一個線程上執行。意思是說,如果你在一個線程上有一個表單,這個表單在另一個引發事件的線程上產生了一些工作,但在這之前,你在表單線程中掛鉤了這個事件,你需要小心,不要直接在事件處理程序,因爲這些事件處理程序正在後臺線程上調用。

最後,從後臺線程正確處理UI的方法是通過調用Invoke並傳入一個代理來執行所需的UI工作。 HTH

3

在WinForms中,您需要在UI線程上調用UI事物,您總是可以檢查您當前正在獲取的UI控件的哪個線程爲InvokeRequired

void ApplyUiChanges() 
{ 
    if(this.InvokeRequired) 
    { 
     this.Invoke(new Action(ApplyUiChanges)); 
     return; 
    } 

    // UI stuff here... 
} 

在WPF techinic是相似的。但是,而不是使用InvokeRequired你應該問CheckAccess()DispatcherObject(所有UI的控件從中獲得)

void ApplyUiChanges() 
{ 
    if (!dispatcherObject.CheckAccess()) 
    {  
     dispatcherObject.Dispatcher.Invoke(DispatcherPriority.Send, new Action(ApplyUiChanges)); 
     return; 
    } 
    // UI stuff here... 
} 

您也可以看看Async CTP,這可能是有用的。但它只是CTP,還沒有發佈。

處理UI線程通信的另一種方法是使用PostSharp。寫(或複製粘貼)GuiThreadAttribute。之後,您將能夠使用這樣的語義:

[GuiThread] 
void ApplyUiChanges() 
{ 
    // UI stuff here... 
} 
2

在WinForms應用程序中,只有一個線程是UI線程。您不想通過長操作來阻止此線程,以便UI始終能夠響應。你也不應該從UI線程以外的任何線程更新任何UI元素。
如果我想從UI執行任何冗長的操作,我總是使用BackgroundWorker。BackgroundWorker的主要優點是它可以報告進度並通過ProgressChanged和RunWorkerCompleted報告它已完成。這兩個事件發生在UI線程中,因此您可以安全地更新任何UI元素,而無需使用InvokeRequired和Invoke。

3

從我所經歷的,「UI線程」是一個用詞不當。沒有一個線程可以處理應用程序的所有UI。爲了簡單起見,在一個線程上使用UI通常是一個好主意,但是沒有任何東西阻止您產生另一個線程,並在該線程上創建新的控件並向用戶顯示它們。重要的是,控制屬性僅在上創建的線程上更改爲。正如另一個人所提到的,通過查看Control.InvokeRequired屬性,可以看到您是否正在該線程中。

如果您所在的線程不是您希望運行的新窗體的線程,而且您不具備在所需線程上創建的控件上下文的豪華感,那麼你必須得到對你想要的線程的System.Threading.SynchronizationContext的引用(我通常通過在靜態變量中存儲來自主線程的System.Threading.SynchronizationContext.Current的引用來實現這一點,但這隻能在線程上創建至少一個控件後才能完成)。該對象將允許您在其主線程上運行委託。

我不得不在一個Windows應用程序中執行此操作,該應用程序還託管了一個WCF服務,並且需要從該服務啓動UI,但我希望它與該UI的其餘部分位於相同的線程中。

HTH, Brian

+0

+1因爲用詞不當,實在令人困惑 – 2013-03-20 11:40:39