2013-03-13 86 views
3

這是我這裏其實我平時解決與真棒 後的數據庫,你可以在這裏找到我的所有問題的第一篇文章。但實際上,我堅持現在:多線程COMObject和UI線程(C#)

我工作的一個項目MVVM包括COM對象以下。 正如我在研究中讀到的,我明白COM對象只能從創建它的線程訪問。我的COM對象實現以下接口

interface IComUpdate 
{ 
    void Update(); 
} 

所以,當我創建我的COM對象,每次有更新(我不知道什麼時候,它的隨機)的COM服務器將調用COM對象類我的Update()執行。

我的目標是創建一個不同的線程,命名一個COM對象線程,其中COM對象獨立於我的UI線程存在,所以每次有更新時,我都會在與UI線程不同的線程中處理它。

其實這是工作:

在我的ViewModel的開始,我創建一個特定的對象的集合。

這個對象,可以把它叫做ModelObj,是模型的一部分,定義了一個靜態構造函數中的應用,除了初始化一些變量,COM對象創建並啓動一個新的線程:

Thread t = new System.Threading.Thread(() => 
      { 
       System.Threading.Thread.CurrentThread.Name = "Thread of COM Object"; 
       IComUpdate myComObj; 
       myComObj = (IComUpdate)Activator.CreateInstance(blabla); 
       Application.Run(); 
      }); 

t.SetApartmentState(ApartmentState.STA); 
t.Start(); 

它實際上工作得很好,在我的COM對象的Update()實現中,我確實看到線程是剛剛創建的線程,而不是UI線程。

現在的問題是:這個ModelObj我創建實現了INotifyPropertyChanged接口。

我的想法如下:每次COM對象接收到更新時,我都會處理來自COM對象線程的數據,並從此線程更新我的ModelObj實例的某些屬性,以便這些屬性將引發屬性更改我的ModelObj和UI線程將更新用戶界面。

如果UI更新需要太多時間,我可能會錯過一些Update()出現在屏幕上,但COM對象將它們記錄在我的ModelObj實例中,因此UI捕獲所有更新並不重要,I只是不希望COM對象不得不等待UI被更新以再次調用。

我讀噸的帖子,然後以爲我RaisePropertyChanged("property")會失敗。

其實即使是在COM對象的線程中,RaisePropertyChanged成功執行,那麼跟蹤我的代碼,我把它切換到我的視圖模型組件,在那裏我做

// Here I'm still in the thread of my COM object! 
base.NotifyOfPropertyChange<string>(() => this.property) 

,然後UI更新。

注:我使用微卡利我在WPF查看和我的視圖模型之間的結合。

所以我可以在此base.NotifyOfPropertyChange<string>(() => this.property)後無法跟蹤。也許Caliburn處理線程切換,這不是我的問題。

我能說的是,我的COM對象線程等待UI更新以在我的RaisePropertyChanged("property")之後到達下一條指令,所以它和UI線程完成整個工作完全一樣。

我希望我的COM對象的線程來更新我的ModelObj將發送發送UI消息更新(因爲這ModelObj的某些領域已經改變),並繼續立刻,不知道如果UI實際上更新或不。

有人對此行爲有了解嗎?

非常感謝。

#### UPDATE ####

謝謝大家對這樣的快速解答。

我確實是因爲Zdeslav Vojkovic建議:

你應該總是從GUI線程

爲了完整更新GUI這裏是我是如何做到:

因爲我的看法是完全WPF背後沒有代碼我沒有任何控件或窗體可以調用BeginInvoke,所以在我的ModelObj的靜態構造函數中,我從UI Thread構建了一個不可見的控件,只是爲了能夠調用BeginInvoke。

因此,我宣佈它:

public static Control mInvokeControl; 
delegate void MyDelegate(); 
private MyDelegate _NotifyDelegate; 

,然後做這在我的對象的靜態構造函數:

mInvokeControl = new Control(); 
mInvokeControl.CreateControl(); 

在正常的構造我初始化委託這種方式:

_NotifyDelegate = new MyDelegate(this.NotifyByInvoke); 

然後我就這樣用它:

ModelObj.mInvokeControl.BeginInvoke(this._NotifyDelegate); 

所述方法的:

public void NotifyByInvoke() 
{ 
    RaisePropertyChanged("Update"); 
} 

一切正常!

回答

3

的COMObj是從創建它

這是不正確的的線程訪問。它取決於對象的公寓模型,但通常你可以從任何線程訪問它,它將在同一線程上調用或編組爲正確的線程。

我相信你的問題是你從後臺線程更新GUI是一個主要的禁忌。您應該始終從GUI線程更新GUI。當你更新模型對象時,它仍然發生在後臺線程上,並且在該線程上觸發INotifyPropertyChanged接口事件。

您需要使用這樣的事情模型更新到GUI線程同步(的WinForms,而不是WPF - WPF中,你應該使用frm.Dispatcher.BeginInvoke但問題是相同的):

私人委託無效ExecuteActionHandler(動作動作) ;

public static void ExecuteOnUiThread(this Form form, Action action) 
{ 
    if (form.InvokeRequired) { // we are not on UI thread 
    // Invoke or BeginInvoke, depending on what you need 
    // but you said ' and continue immediatly' so BeginInvoke it is 
    form.BeginInvoke(new ExecuteActionHandler(ExecuteOnUiThread), action); 
    } 
    else { // we are on UI thread so just execute the action 
    action(); 
    } 
} 

another question with similar problem,我在那裏提供了額外的細節。

+0

「COMObj只能從創建它的線程訪問」,你其實是對的。我只是爲其他人添加評論: 如果我讓我的新線程MTA,COMObj駐留在主線程(因爲它是第一個調用CoInitialize類似的東西,我明白)。但ThreadingModel(請參閱註冊表編輯器)是Apartment,這就是爲什麼在我的具體情況中這是真的。 – user2164703 2013-03-13 10:35:28

+0

我只是有一個剩餘的問題,假設我調用BeginInvoke過程,但正如我所說,它需要更多的時間來更新UI比處理數據,我會做其他BeginInvoke調用之前的行動終止在UI線程上。我的通話是否丟失或排隊?我寧願讓它迷路。 – user2164703 2013-03-13 12:51:00

+0

不確定WPF,因爲我沒有使用它,但是對於WinForms來說,這些調用被確定,因爲它們實際上是用PostMessage處理的。如果WPF表單不會使用某種類似的技術,我會感到驚訝 - 確保這些調用不會丟失,但我不確定執行順序是否有保證。在這裏看到更多:http://stackoverflow.com/a/2411183/1663919 – 2013-03-13 12:55:10

0

我不知道你處理了多少數據,或者執行GUI部件需要多少時間。您也可以考慮使用鎖定的隊列。你可以在你的ModelObj中使用一個隊列來排隊新的任務。這你做你得到的一切。然後你可能有一個計時器線程(在GUI線程上)。

在這裏,您只需檢查鎖定隊列,是否有一些新的數據顯示在GUI上。您可以在本地出列完整列表。然後,您還可以檢查是否有多個數據要顯示在一個組件上。這樣你可以跳過更新,你已經有了更新的更新。 然後您可以跳過調用gui線程執行操作的時間。您可以一次執行多個GUI更新。如果你有太多的事情要做,那麼你可能只允許出列特定數量的項目,讓GUI對用戶交互作出反應。但是,您需要檢查隊列是否持續增長。

+0

感謝您的回答,我確實考慮過您描述的內容,但不想使用計時器。我的數據在幾秒鐘內用C++中的高優化庫進行處理,UI更新使其爆炸到1到20毫秒。 (它的粒子物理應用程序) – user2164703 2013-03-13 11:24:12

+0

我的意思是隻使用GUI的定時器,而不是數據處理。您也可以將您的粒子收集到二維數組中(X,Y位置),並且可以將它保存在時間片中。然後你可以定義一個平均窗口例如0.5秒。您可以在數據線程中填充數組,並將渲染的組合圖像不時地推送到GUI。 – Patrick 2013-03-13 12:10:11

+0

是的,我已經使用緩衝區實際上通知我的圖形用戶界面每X條目,定時器將允許我定期更新,它的一個想法,我會看看它,或使其成爲一個選項。 THKS。 – user2164703 2013-03-13 12:30:26