简体   繁体   English

使用Matlab引擎的多线程C ++应用程序

[英]Multithreaded C++ application using Matlab Engine

I open Matlab engine in an initialization thread, doing : 我在初始化线程中打开Matlab引擎,这样做:

bool MY_MATLAB_ENGINE_o::Open()
{
    // Handle the case where engine is already open
    if( MatlabEngine )
    {
        return true;
    }
    else if( !( MatlabEngine = engOpen( 0 ) ) )
    {
        return false;
    }

    IsEngineOpen.SetValue( true );
    return true;
}

Function engOpen() opens a COM channel to Matlab. 函数engOpen()打开一个通往Matlab的COM通道。 Once the engine is open, the thread falls in wait event mode. 引擎打开后,线程将进入等待事件模式。

Then, in another thread, I do this : 然后,在另一个线程中,我这样做:

bool MY_MATLAB_ENGINE_o::ChangeDirectory( QString strPath )
{
    QString strPathToScript = "cd('" + strPath + "');";

    QByteArray ba = strPathToScript.toLatin1();
    const char* cPathToScript = ba.data(); 

    if( MatlabEngine )
    {
        engEvalString( MatlabEngine, cPathToScript );

        return true;
    }

    return false;
}

I get a CoInitialize has not been called first-chance exception on engEvalString( MatlabEngine, cPathToScript ); 我在engEvalString( MatlabEngine, cPathToScript );CoInitialize has not been calledCoInitialize has not been called优先机会异常engEvalString( MatlabEngine, cPathToScript ); which seems to tell me that Matlab COM server isn't available (but Matlab engine is still up and running). 这似乎告诉我Matlab COM服务器不可用(但是Matlab引擎仍在运行)。

When I put everything in the same thread it works fine though, but that's not the kind of design I had in mind. 当我将所有内容放到同一线程中时,它仍然可以正常工作,但这不是我想到的那种设计。

I find Matlab engine documentation lack information about engine+COM. 我发现Matlab引擎文档缺少有关engine + COM的信息。 Any idea how to have engine initialization and function calls in separated threads ? 知道如何在单独的线程中进行引擎初始化和函数调用吗?

Thanks ! 谢谢 !

EDIT following RobH's answer 按照RobH的答案进行编辑

I added this method to my class (instantiated in the second thread) : 我将此方法添加到我的类中(在第二个线程中实例化):

bool MY_MATLAB_FUNCTION_CALL_o::PostThreadCreationHook()
{
    HRESULT hr;
    hr = CoInitializeEx(0, COINIT_MULTITHREADED); 
    if (FAILED(hr)) 
    { 
        return false;
    }

    return true;
}

And now when I call engEvalString( MatlabEngine, cPathToScript ); 现在,当我调用engEvalString( MatlabEngine, cPathToScript ); I get The application called an interface that was marshalled for a different thread first-chance exception :) I have so much fun this morning ! 我得到了The application called an interface that was marshalled for a different thread优先机会The application called an interface that was marshalled for a different thread :)今天早上我玩得很开心! :) :)

So, CoMarshalInterface() ? 那么, CoMarshalInterface()

CoInitialize has to be called from every thread where you use a COM object, not just the main thread. 必须从使用COM对象的每个线程(而不仅仅是主线程)调用CoInitialize。

It has been a decade since I last automated Matlab, so excuse rustiness in what follows. 自从我上一次使Matlab自动化以来已经有十年了,所以请原谅接下来的生锈。 That you have received the CoInitialize error suggests that the engOpen call wraps underlying COM calls. 您收到了CoInitialize错误,表明engOpen调用包装了基础的COM调用。 Unfortunately, this exposes you unawares to the can of worms that is COM. 不幸的是,这使您不知道是COM蠕虫病毒。 I guess you are right and that engOpen includes a call to CoInitialize, which initialises the COM library on the current thread. 我猜您是对的,并且engOpen包含对CoInitialize的调用,该调用会在当前线程上初始化COM库。 To access COM objects from a thread CoInitialize must always have been called on that thread before any calls to COM (other than one permitted COM function, I forget which.) 要从线程访问COM对象,必须始终在对该线程的任何调用之前已在该线程上调用CoInitialize(除了一个允许的COM函数,我忘记了。)

My advice is to isolate all of your Matlab calls onto one thread now. 我的建议是现在将所有Matlab调用隔离到一个线程中。 If you do that you won't have to make the explicit CoInitialize call, and you avoid any later multithreaded COM issues. 如果这样做,则不必进行显式的CoInitialize调用,并且可以避免以后出现任何多线程COM问题。 You might get your program working today by calling CoInitialize on the second thread but one day you'll be bitten by another COM problem. 您今天可以通过在第二个线程上调用CoInitialize来使程序正常工作,但是有一天您会被另一个COM问题所困扰。

[I spent about a decade elbows-deep on COM and it is full of bear traps. [我在COM上花了大约十年的弯头,它充满了陷阱。 You could spend a few weeks reading up on a technology which Microsoft tried to hide / kill with .Net, but it is better just to take the easy (single-thread) path now and forget about it.] 您可能需要花几周的时间来阅读Microsoft尝试使用.Net隐藏/杀死的技术,但是最好还是现在就走简单的(单线程)路径,而不必理会它。]

Update I'm afraid your edit has tripped you into the mire of COM threading models. 更新恐怕您的编辑使您陷入了COM线程模型的泥潭。 COINIT_MULTITHREADED effectively tells COM that you're going to take care of all of the little nuances of threading, which is almost certainly not what you want to do. COINIT_MULTITHREADED有效地告诉COM您将处理线程的所有细微差别,这几乎肯定不是您想要做的。 COM operates with several (last time I paid attention it was three) threading models and the parameter you pass to CoInitializeEx declares which of those models you wish to use. COM使用多个(上次我注意的是三个)线程模型进行操作,并且您传递给CoInitializeEx的参数声明了要使用的那些模型。

Apologies to all if what follows is slightly off. 如果出现以下情况,请向所有人道歉。

If you specify COINIT_MULTITHREADED you need to either know that the COM object you are calling is thread-safe or do the appropriate locking (and marshalling of interfaces and data between threads) yourself. 如果指定COINIT_MULTITHREADED,则需要知道您正在调用的COM对象是线程安全的,或者您自己进行适当的锁定(以及线程之间的接口和数据的编组)。

COINIT_APARTMENTTHREADED, which is probably what engOpen uses as in my experience it's the most common, lets the COM library deal with multithreading for you. COINIT_APARTMENTTHREADED(可能是engOpen所使用的,因为根据我的经验,这是最常见的),它使COM库可以为您处理多线程。 The library may, for instance, create proxy and stub objects to mediate calls across threads (or process boundaries, which is what will happen when you call to Matlab.) 例如,该库可以创建代理和存根对象,以调解线程之间的调用(或进程边界,这是您调用Matlab时会发生的情况)。

engOpen created a Matlab proxy object on your main thread. engOpen在您的主线程上创建了一个Matlab代理对象。 This proxy object can be called from the thread where it was created and, if I recall correctly, from any other thread in the 'apartment' (where CoInitializeEx has been called with COINIT_APARTMENTTHREADED.) You have tried to call through the proxy from a thread in a different threading model, the COM library has noticed, and has issued the error you mention. 可以从创建它的线程中调用此代理对象,如果我没记错的话,也可以从“公寓”中的任何其他线程(已使用COINIT_APARTMENTTHREADED调用CoInitializeEx的线程)中调用。在不同的线程模型中,COM库注意到了并发出了您提到的错误。

In many ways COM is amazing, but the intricacies are a pain in the arse. 从很多方面来说,COM都是惊人的,但是复杂性却使人感到痛苦。 Be thankful you are never likely to have to use Distributed COM, which is truly nasty! 值得庆幸的是,您永远不必使用Distributed COM,这实在令人讨厌!

Update 2 My ancient memory of COM threading models is wrong. 更新2我对COM线程模型的古老记忆是错误的。 This MSDN page states that there is one thread per apartment with COINIT_APARTMENTTHREADED. 此MSDN页面指出,每个公寓有一个线程具有COINIT_APARTMENTTHREADED。 COM objects can be accessed using the same interface pointer from all threads in the apartment where they were created. 可以使用同一接口指针从创建对象的单元中的所有线程访问COM对象。 For COINIT_APARTMENTTHREADED that means just the thread where the object was created. 对于COINIT_APARTMENTTHREADED,这意味着仅创建对象的线程。 In COINIT_MULTITHREADED that would be all the threads in the multithreaded apartment but (1) you don't get to choose which thread the Matlab engine is created on if you use engOpen and (2) trying to call a COM object that you didn't write from a multithreaded apartment is risky. 在COINIT_MULTITHREADED中,这将是多线程单元中的所有线程,但是(1)如果使用engOpen,您将无法选择创建Matlab引擎的线程;(2)尝试调用未使用的COM对象从多线程单元进行写操作是有风险的。 The original OLE threading model only allowed COM calls from the main GUI thread, BTW. 原始OLE线程模型仅允许从主GUI线程BTW进行COM调用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM