2013-03-13 88 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