2013-06-28 87 views
1

背景:C#從靜態類訪問函數的調用線程之外

我對此需要超過幾秒鐘來完成更多的多個操作的類。在此期間,我想更新UI。所以BackgroundWorker通常是最好的選擇。但由於某種原因,BackGroundWorker並不總是以我想要的方式工作(例如:當我嘗試對事件使用WebBrowser並調用ReportProgress事件時,BackgroundWorker看起來崩潰)。

因此,我通過將Ui從主線程分離來避免所有這些。

此僞更好地解釋它:

public Ui ui; 

main 
{ 
    Thread threadUi = new Thread(initiateUi);  
    //Initiate and start Thread 

    //Everything I will do from here on will not have any consequences 
    //on my ui. 
    // 
    //Object Ui can still be publicly accessed, making it possible to 
    //update the user interface. 
} 

現在,當我有Bar類的一個實例,我將使它的UI這樣的訪問:

public Bar bar1; 
public Bar bar2; 

main 
{ 
    // 
    //other stuff here 
    // 

    Thread threadBar1 = //New Thread where I call the Bar initializer function 
         //and pass bar1 as parameter. 
    Thread threadBar2 = //idem dito, except with bar2 as parameter 

    // 
    //other stuff here 
    // 
} 

有了這個設計,我可以打電話bar1和bar2從我的用戶界面使用以下功能:

Program.bar1.someFunction(); 

問題:

現在讓我們說我有一個名爲FooHandler的類。該類有一個函數,用於在某個FooDepository中搜索Foo的所有實例,以及其他函數來操縱Foo對象。這是一個靜態類,因爲在我的情況下,它不需要有多個實例。

但是,如果我要從FooHandler調用一個函數,該函數將在我的UI線程中運行,因爲這是調用線程(我不太確定,但我找不到有關此主題的任何文檔)。所以我很有可能面臨我開始的問題。

問題:

是否有可能訪問靜態類的功能,而無需使用來自調用線程的處理能力?

回答

2

可以通過使用另一個線程調用此函數。如果您使用.NET 4,請查看Task對象,這將輕鬆解決問題。例如,如果函數返回字符串,則需要調用函數的Task<string>。然後根據你的邏輯,你會阻止,直到它完成或做類似的事情。如果您使用的是.NET 4.5,那麼使用異步/等待更容易。

+0

我正在使用.Net 4.0。所以我會研究這個Task對象。 編輯:哇!使用任務就像下面這樣簡單: 任務t =新任務(new Action(()=> {FooHandler.someFunction();}));? – Jordy

+1

是的,這很容易:)很高興幫助。請使用通用版本的任務,然後你可以做t.Result(這將阻止線程,所以要小心)返回你的類型。此外,您不需要執行新的操作,下面是一個示例:任務 task = new任務(()=> FooHandler.someFunctionThatReturnsString()); –

3

首先:方法範圍(定義它的地方)與程序流程沒有任何關係。在何處定義方法(FooHandler,BarProvider或ThreadX)不會影響其調用的位置。實際上,方法總是在調用者的線程中調用。

因爲您沒有提及任何模型,視圖或視圖模型,並在標題中顯示「c#」,我假設您在談論WinForms。

在WinForms UI控件需要從用於創建它們的線程(通常是主線程)調用(更新)。所有的UI控件都實現了ISynchronizeInvoke接口,這意味着要做到這一點。因此,而不是常規的:

progress.Position = 7; 

你需要調用Invoke

progress.Invoke(new Action(() => progress.Position = 7), null) 

因爲有很多的樣板代碼,我寫了一個小的擴展功能,用於自己:

public static class ControlExtensions 
{ 
    public static void Synchronize(this Control control, Action action) 
    { 
     if (control == null || !control.InvokeRequired) 
     { 
      action(); 
     } 
     else 
     { 
      control.Invoke(action, null); 
     } 
    } 
} 

所以現在你可以:

progress.Synchronize(() => progress.Position = 7); 

(打字少一點,易於閱讀)

從技術上講,ISynchronizeTarget上的調用並不真正調用給定的動作。它只是在消息隊列中放入一條消息(好的舊WM_xxxx)(但是在調用者的線程中這樣做),委託作爲參數。然後,如果目標(控制)線程正在處理消息(在它自己的線程中),它將獲取此WM_xxxx消息,並調用委託(在調用者線程中 - 但這次是UI線程)並返回。

如果你需要新的線程調用FooHandler,你不想等待使用任務(它可能是最簡單的方法):它不會等待(不會阻止用戶界面

Task.Factory.StartNew(() => FooHandler.SearchOrWhatever(...)); 

線)。

儘管所有這一切都說了,不要以爲它已經完成。 多線程很難。而所有那些支持的構造可以幫助您節省打字的時間,但困難的部分仍然存在:死鎖,競態條件,捱餓等。

+0

對於'方法範圍(它被定義的地方)+1與程序流程無關「作爲第一行。 –