多线程COMObject和UI线程(C#) [英] MultiThreading COMObject and UI Thread (C#)

查看:208
本文介绍了多线程COMObject和UI线程(C#)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的第一篇文章,实际上我通常解决所有我的问题与真棒
post数据库,你可以找到这里。但我现在卡住了:



我正在处理一个项目,下面的MVVM包括一个COM对象。
正如我在研究期间读到的,我理解COM对象只能从创建它的线程访问。我的COM对象实现以下接口

  interface IComUpdate 
{
void Update
}

所以当我创建我的COM对象时,每次有更新不知道什么时候,它的随机)COM服务器将调用我实现的COM对象类的 Update()



我的目标是创建一个不同的线程,命名一个COM对象线程,其中COM对象独立存在于我的UI线程,所以每次有一个更新,我处理它在不同的线程比UI线程。 p>

实际上它是工作的:



在我的ViewModel开始,我创建一个特定对象的集合。 p>

这个对象叫做 ModelObj ,是模型的一部分,定义了一个静态构造函数,从初始化一些变量,为COM对象创建和启动一个新的线程:

 线程t = new System.Threading.Thread ()=> 
{
System.Threading.Thread.CurrentThread.Name =COM对象的线程;
IComUpdate myComObj;
myComObj =(IComUpdate)Activator.CreateInstance(blabla);
Application.Run();
});

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

它实际上效果非常好,在 Update $ c>我的COM对象的实现,我实际上看到的线程是一个刚刚创建,而不是UI线程。



现在的问题是这个:this ModelObj 我创建实现 INotifyPropertyChanged 接口。



以下:每次COM对象接收更新,我处理来自COM对象线程的数据,并更新我的 ModelObj 实例从这个线程的一些属性,因此这些属性然后提高我的 ModelObj 的属性更改,UI线程将更新用户界面。



花费太多时间,我可能会错过一些 Update()出现在屏幕上,但COM对象将它们记录在我的 ModelObj 实例,所以它不是非常重要的UI捕获所有的更新,我只是不想要COM对象必须等待UI更新,以便再次调用。



我读了许多帖子,然后认为我的 RaisePropertyChanged(property)会失败。



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

  //这里我仍然在我的COM对象的线程中! 
base.NotifyOfPropertyChange< string>(()=> this.property)

那么UI更新。



注意:我使用Caliburn Micro在WPF中的View和ViewModel之间进行绑定。

所以我不能追踪这个 base.NotifyOfPropertyChange< string>(()=> this.property)。也许Caliburn处理线程切换,这不是我的问题。



我可以说是我的COM对象线程等待UI更新到下一个在 RaisePropertyChanged(property)之后的指令,因此它与UI线程完成整个工作完全相同。



我希望我的COM对象线程更新我的 ModelObj ,它会发送给UI发送消息更新(因为这个 ModelObj 已更改),并立即继续立即,而不知道UI是否真的更新。



关于此行为的想法?



非常感谢。



#### UPDATE ##



感谢大家提供快速解答。



我确实是Zdeslav Vojkovic :



您应该总是从GUI线程更新GUI



为了完整性,这里是我做的:



因为我的View是完整的WPF没有代码后面我没有任何控件或窗体被调用BeginInvoke从,所以在我的ModelObj的静态构造函数,我构建了一个不可见的控件从UI线程只是为了能够调用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);
}

一切正常!



$ b


b

这不是真的。它取决于对象的公寓模型,但通常你可以从任何线程访问它,它将被调用在同一个线程或封送到正确的线程。



我相信你的问题是你从后台线程更新GUI是主要的no-no。你应该总是从GUI线程更新GUI。当你更新模型对象时,它仍然发生在后台线程和 INotifyPropertyChanged 接口在该线程上触发的事件。



你需要通过使用这样的东西(WinForms,而不是WPF)同步模型更新到GUI线程 - 在WPF中,你应该使用 frm.Dispatcher.BeginInvoke 但问题是一样的):



私人代理void ExecuteActionHandler(Action action);

  public static void ExecuteOnUiThread(this Form form,Action action)
{
if(form.InvokeRequired){//我们不在UI线程
//调用或BeginInvoke,取决于你需要
//但你说'并继续立即'所以BeginInvoke它是
form.BeginInvoke(new ExecuteActionHandler(ExecuteOnUiThread),action);
}
else {//我们在UI线程上,所以只是执行动作
action();
}
}

有类似问题的另一个问题,我已提供了其他详细信息。


This is my first post here as actually i usually solve all my issue with the awesome post database you can find here. But I'm actually stuck right now:

I'm working on a project following the MVVM including a COM object. As I read during my research, I understand that the COM object is only accessible from the thread which created it. My COM object implements the following interface

interface IComUpdate
{
    void Update();
}

So when I create my COM object, each time there is an update (I dont know when, its random) the COM Server will call the Update() of the COM object class I did implement.

My goal was to create a different thread, naming a COM object thread, where the COM object exist independantly of my UI Thread, so everytime there is an update, I handle it in a different thread than the UI Thread.

Actually it is working:

At the Beginning of my ViewModel I create a collection of a specific object.

This object, lets call it ModelObj, is part of the model and defines a static constructor in which the application, apart from initializing some variables, creates and starts a new thread for the COM object:

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();

It actually works very well, in the Update() implementation of my COM object, I actually see that the thread is the one just created and not the UI thread.

Now the issue is this: this ModelObj I create implements the INotifyPropertyChanged interface.

My thinking was the following: each time the COM object receives an update, I handle data from the COM object thread, and update some property of my ModelObjinstance from this thread, so these properties will then raise the property change of my ModelObj and the UI thread will update the User Interface.

If the UI update takes too much time, I might miss some Update() to appear on the screen but the COM object will have them recorded in my ModelObj instance so it is not very important that the UI catch all the updates, I just didnt want the COM object to have to wait for the UI to be updated to be called again.

I read tons of posts and thought then that my RaisePropertyChanged("property") would fail.

Actually even in the COM object's thread, the RaisePropertyChanged successfully executes, so tracing my code, I see it switches to my ViewModel assembly where I do

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

and then the UI Update.

Note: I'm using Caliburn Micro for binding between my View in WPF and my ViewModel.

So I can't trace after this base.NotifyOfPropertyChange<string>(() => this.property). Maybe Caliburn handles the thread switch, this is not really my issue.

What I can say is that my COM object thread waits for the UI to update to get to the next instruction after my RaisePropertyChanged("property"), so it's exactly the same as if the UI thread did the whole work.

I want my COM object thread to update my ModelObj which will send to send the UI a message to update (because some fields of this ModelObj have changed) and continue immediatly, without knowing if the UI actually updates or not.

Does someone got an idea about this behaviour?

Thank you very much.

####UPDATE####

Thanks everyone for such quick answers.

I did actually as Zdeslav Vojkovic suggested :

You should always update GUI from GUI thread

For completeness here is how I did:

Because my View is full WPF with no code behind i dont have any controls or form to be call BeginInvoke from, so in the static constructor of my ModelObj, I built an invisible Control from the UI Thread just to be able to call BeginInvoke on it.

So i declared it :

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

and then did this in the static constructor of my Object:

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

in the normal constructor i Initialize the delegate this way:

_NotifyDelegate = new MyDelegate(this.NotifyByInvoke);

Then after i just use it this way:

ModelObj.mInvokeControl.BeginInvoke(this._NotifyDelegate );

With the method being:

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

Everything works fine !

解决方案

the COMObj is only accessible from the thread which created it

this is not true. It depends on objects apartment model, but usually you can access it from any thread and it will be either called on same thread or marshaled to proper thread.

I belive that your problem is that you update GUI from background thread which is a major no-no. You should always update GUI from GUI thread. When you update your model object, it still happens on background thread and event of INotifyPropertyChanged interfaces fires on that thread.

You need to synchronize model update to GUI thread by using something like this (WinForms, not WPF - in WPF you should use frm.Dispatcher.BeginInvoke but the problem is the same):

private delegate void ExecuteActionHandler(Action action);

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();
  }
}

There is another question with similar problem and I have provided additional details there.

这篇关于多线程COMObject和UI线程(C#)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆