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

时间:2013-10-08 14:28:31

标签: c++ multithreading matlab com matlab-engine

我在初始化线程中打开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;
}

函数engOpen()打开一个到Matlab的COM通道。 一旦引擎打开,线程就会进入等待事件模式。

然后,在另一个主题中,我这样做:

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

我在CoInitialize has not been called上得到engEvalString( MatlabEngine, cPathToScript );第一次机会异常,似乎告诉我Matlab COM服务器不可用(但是Matlab引擎仍在运行)。

当我把所有东西放在同一个线程中时,它工作得很好,但这不是我想到的那种设计。

我发现Matlab引擎文档缺少有关引擎+ COM的信息。知道如何在分离的线程中进行引擎初始化和函数调用吗?

谢谢!

编辑跟随RobH的回答

我将此方法添加到我的类中(在第二个线程中实例化):

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

    return true;
}

现在当我致电engEvalString( MatlabEngine, cPathToScript );时,我得到The application called an interface that was marshalled for a different thread第一次机会异常:)今天早上我玩得很开心! :)

那么,CoMarshalInterface()

1 个答案:

答案 0 :(得分:1)

必须从使用COM对象的每个线程调用CoInitialize,而不仅仅是主线程。

自从我上次自动化Matlab以来已经十年了,所以请原谅下面的生锈。您收到CoInitialize错误表明engOpen调用包装了基础COM调用。不幸的是,这会让你不知道COM的蠕虫病毒。我猜你是对的,并且engOpen包括对CoInitialize的调用,它在当前线程上初始化COM库。要从线程访问COM对象,在任何对COM的调用之前,必须始终在该线程上调用 <(除了一个允许的COM函数,我忘了它。)

我的建议是立即将所有Matlab调用隔离到一个线程上。如果这样做,您将不必进行显式的CoInitialize调用,并避免任何后来的多线程COM问题。您可以通过在第二个线程上调用CoInitialize来使您的程序正常工作,但有一天您会被另一个COM问题所困扰。

[p> [我花了大约十年的时间肘部深处COM,它充满了熊陷阱。你可能花几周的时间阅读一下微软试图用.Net隐藏/杀死的技术,但现在最好采取简单的(单线程)路径而忘记它。]

<强>更新 我担心你的编辑会让你陷入COM线程模型的泥潭。 COINIT_MULTITHREADED有效地告诉COM您将要处理线程的所有细微差别,这几乎肯定不是您想要做的。 COM操作时有几个(上次我注意它是三个)线程模型,你传递给CoInitializeEx的参数声明了你想要使用哪些模型。

如果接下来的内容稍微偏离,请向所有人道歉。

如果指定COINIT_MULTITHREADED,您需要知道您调用的COM对象是线程安全的,或者自己进行适当的锁定(以及线程之间的接口和数据编组)。

COINIT_APARTMENTTHREADED,这可能是engOpen在我的经验中使用它最常见的,让COM库为你处理多线程。例如,库可以创建代理和存根对象来调解跨线程(或进程边界)的调用,这是调用Matlab时会发生的情况。)

engOpen在主线程上创建了一个Matlab代理对象。可以从创建它的线程调用此代理对象,如果我没记错的话,可以从'apartment'中的任何其他线程调用(其中CoInitializeEx已使用COINIT_APARTMENTTHREADED调用。)您试图通过线程调用代理在不同的线程模型中,COM库已经注意到,并且已经发出了您提到的错误。

在许多方面,COM是惊人的,但复杂的是屁股的痛苦。要感谢你永远不可能使用分布式COM,这真的很讨厌!

更新2 我对COM线程模型的古老记忆是错误的。 This MSDN page声明每个公寓有一个主题为COINIT_APARTMENTTHREADED。可以使用来自创建它们的公寓中所有线程的相同接口指针来访问COM对象。对于COINIT_APARTMENTTHREADED,这意味着只创建对象的线程。在COINIT_MULTITHREADED中,这将是多线程单元中的所有线程但是(1)如果使用engOpen并且(2)尝试调用不是的COM对象,则无法选择创建Matlab引擎的线程从多线程公寓写信是有风险的。原始的OLE线程模型只允许来自主GUI线程的BT调用。