2010-10-26 88 views
5

我是C#和多線程的新手,所以我很抱歉,如果這是一個重複的問題,但作爲一個新手它看起來我的問題與我讀過的其他人略有不同。C#多線程 - 更新與背景事件的GUI

我的GUI在一個(主)線程中運行。它調用一個後臺任務(在一個dll中 - 我也在寫),它運行在一個單獨的線程中。 dll對GUI不瞭解(即不能引用GUI類)。

現在,假設我想根據dll線程的狀態更新GUI上的進度條 - >我正在做的是在dll中創建一個事件,每X%會觸發並且GUI將會訂閱此事件。當事件被觸發時,GUI將更新進度條。

我的問題:

  1. 是創建活動的最佳方式(銘記DLL不能引用GUI)的方法?
  2. 如何確保我的上述方法是「事件安全」?我是否應該在事件中通過進度百分比以保證線程安全或者是否還有更多內容?
  3. 更新GUI時是否需要使用Invoke?我看到一篇文章提示我做了,但我不明白爲什麼自從GUI線程完成了更新吧!

希望你能澄清我的這個!

謝謝

+0

「安全事件」是什麼意思? – 2010-10-26 16:55:16

+0

請在此處查看「BackgroundWorker」類的註釋:http://stackoverflow.com/questions/1506838/backgroundworker-vs-background-thread/1507337#1507337 – 2010-10-26 18:24:10

+0

我只想說感謝所有答覆。他們都幫我回答了我的問題。很高興看到這樣的幫助! – SimpleOne 2010-10-26 19:34:03

回答

3

1 - 我用這種方法所有的時間和肯定它會工作

2,只需通過一個int值以事件處理程序和變量將是安全的閱讀。然而,當你從代碼fireing事件做這樣的

private void UpdatePercentage(int a) 
{ 
    var myEvent = PercentageUpdatedEvent 
    if(myEvent != null) 
     myEvent(this, new ProgressBarEventArgs(a)); 
} 

這樣做的原因是如此,如果事件被空支票和喊你不會得到一個例外之間unsubcribed。

3.-正如其他人所提到的,你將需要調用Invoke,因爲事件將在dll的線程上運行。然而,通過控件,在沒有EndEnvoike的情況下調用BeginInvoke是合法的,因此調用將不會在dll的線程中阻塞。

這是我一直使用

+0

這確實有幫助。非常感謝。它終於點擊了(可能對你們都很明顯),即使事件處理程序是在GUI類中編寫的,它仍然在dll線程中運行,因爲它是從那裏'調用'的!現在我明白了爲什麼我們需要Invoke,並澄清了它。我同意其他職位,我需要了解爲什麼/如何工作,而不是使用BackgroundWorker等。 – SimpleOne 2010-10-26 19:32:59

3

看看BackgroundWorker類。這聽起來很適合你的場景。

MSDN上此鏈接介紹如何使用它:http://msdn.microsoft.com/en-us/library/cc221403%28VS.95%29.aspx

+2

BackgroundWorker試圖幫助您解決運行後臺操作的任務,而不需要任何有關線程和UI如何工作的真實知識,但是如果確實瞭解事件如何引發以及如何使用Dispatcher,則無法提供多少幫助。我強烈建議任何想要做多線程代碼的人至少試圖理解抽象背景下的情況,比如後臺工作者。 – SoftMemes 2010-10-26 17:14:00

+0

我同意重要的是要弄清楚發生了什麼,但我認爲BackgroundWorker可能是最安全的方式來實現它,如果你不明白髮生了什麼。 – Dismissile 2010-10-26 17:21:10

0

要回答(3),你將需要使用調用。事件處理程序將從後臺線程運行,而不是GUI線程。

3

請記住,在大多數情況下,後臺任務引發的事件也可以在後臺線程上運行。根本沒有線程上下文切換自動發生。

要理解爲什麼,你必須考慮事件是什麼;只是某種類型的Delegate對象。您正在爲主線程設置該事件的委託...但該委託實際上將在後臺線程中觸發事件的代碼中調用。

所以是的;您需要確保您正在將事情移動到該事件處理程序內的GUI線程上運行。

+0

指出它:「但該代表實際上將在後臺線程中,在觸發事件的代碼中調用。」 – SimpleOne 2010-10-26 19:35:12

0

如果你分離一個線程,你需要創建一個委託,它可以安全地用適當的參數調用你的主線程。

delegate void UpdateDelegate(int val) 
void Update(int val) 
{ 
    if(this.InvokeRequired()) 
    { 
    Invoke(new UpdateDeleage(Update),new object[] {val}); 
    return; 
    } 
    this.MyProgressBar.Value = val; 
} 

從您的單獨線程調用更新,就像您從主線程調用它一樣。一旦線程確定您的主線程需要調用來傳遞該值,它將使用您的委託與您傳遞的參數一起調用它。否則,它會簡單地跳過該塊並設置您的值。

例如

... 

new Thread(()=>IncrementValues()).Start(); 

... 

void IncrementValues() 
{ 
    while(true) 
    Update(new Random(0,10)); 
} 
0

模式我有一個on my blog幾種不同的方法解決這個問題,有各自的優勢/劣勢。總之,我建議使用Task類。