多线程协同对象和UI线程(C#)

多线程协同对象和UI线程(C#),c#,multithreading,user-interface,com,C#,Multithreading,User Interface,Com,这是我在这里的第一篇帖子,因为实际上我通常都是用很棒的工具来解决我所有的问题 您可以在这里找到post数据库。但我现在真的被卡住了: 我正在做一个MVVM项目,包括一个COM对象。 正如我在研究期间所读到的,我知道COM对象只能从创建它的线程访问。我的COM对象实现以下接口 interface IComUpdate { void Update(); } 因此,当我创建COM对象时,每次有更新(我不知道什么时候,它是随机的),COM服务器都会调用我实现的COM对象类的update() 我

这是我在这里的第一篇帖子,因为实际上我通常都是用很棒的工具来解决我所有的问题 您可以在这里找到post数据库。但我现在真的被卡住了:

我正在做一个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捕获所有更新不是很重要,我只是不希望COM对象必须等待UI更新后再次调用

我读了很多帖子,然后想我的
RaisePropertyChanged(“属性”)
会失败

实际上,即使在COM对象的线程中,
RaisePropertyChanged
也会成功执行,因此跟踪我的代码时,我看到它会切换到我的ViewModel程序集

// Here I'm still in the thread of my COM object!
base.NotifyOfPropertyChange<string>(() => this.property)
然后在我的对象的静态构造函数中执行此操作:

mInvokeControl = new Control();
mInvokeControl.CreateControl();
在普通构造函数中,我以以下方式初始化委托:

_NotifyDelegate = new MyDelegate(this.NotifyByInvoke);
ModelObj.mInvokeControl.BeginInvoke(this._NotifyDelegate );
然后,在我这样使用它之后:

_NotifyDelegate = new MyDelegate(this.NotifyByInvoke);
ModelObj.mInvokeControl.BeginInvoke(this._NotifyDelegate );
其方法为:

public void NotifyByInvoke()
{
    RaisePropertyChanged("Update");
}
一切正常


COMObj只能从创建它的线程访问

事实并非如此。它取决于对象单元模型,但通常您可以从任何线程访问它,它将在同一线程上调用或封送到适当的线程

我相信您的问题在于您从后台线程更新GUI,这是一个主要的禁忌。您应该始终从GUI线程更新GUI。更新模型对象时,它仍然发生在后台线程上,并且在该线程上触发
INotifyPropertyChanged
接口事件

您需要使用类似的方法将模型更新同步到GUI线程(WinForms,而不是WPF-在WPF中,您应该使用
frm.Dispatcher.BeginInvoke
,但问题是相同的):

私有委托void ExecuteActionHandler(操作)


这里有,我已经提供了更多的细节。

我不知道您处理了多少数据,或者执行GUI部分需要多少时间。您也可以考虑使用锁定队列。您可以使用ModelObj中的队列将新任务排队。这是你用你得到的一切做的。然后您可能有一个计时器线程(在GUI线程上)

在这里,您只需检查锁定的队列,是否有一些新数据显示在GUI上。您可以在本地将完整列表出列。然后,您还可以检查是否有多个数据显示在一个组件上。通过这种方式,您可以跳过已经有更新的更新。
您可以跳过调用gui线程来执行操作的时间。您可以一次执行多个GUI更新。如果您有太多的事情要做,您可能只会排到特定数量的项目,以便GUI对用户交互做出反应。但是,您需要检查队列是否在不断增长。

“只能从创建它的线程访问COMObj”,实际上您是对的。我只是为其他人添加注释:如果我创建了新的线程MTA,那么COMObj将驻留在主线程中(因为它是第一个调用类似于我所理解的东西的CoInitialize)。但是ThreadingModel(请参见注册表编辑器中的)是公寓式的,这就是为什么在我的具体案例中是如此。我只是有一个剩余的问题,假设我调用BeginInvoke过程,但正如我所说,更新UI比处理数据需要更多的时间,在UI线程上终止上一个操作之前,我将进行另一个BeginInvoke调用。我的电话是丢失了还是排队了?我宁愿它丢失。我不确定WPF是否使用它,因为我不使用它,但对于WinForms,调用是被禁止的,因为它们实际上是用PostMessage处理的。如果WPF表单不使用类似的技术,我会感到惊讶——可以肯定调用不会丢失,但我不确定执行顺序是否得到保证。请参阅此处的更多信息:最后,我决定删除对不可见控件的使用,并使用Dispatcher类:我有一个静态变量,它通过DispatcherOperation获取BeginInvoke的结果,我通过status属性检查操作是否完成,如果不是,我会发送更新,但会使用DispatcherPriority。不活动,因此会发送消息,但不会发生任何事情。谢谢你的回答,我确实考虑了你描述的内容,但不想使用计时器