我在初始化线程中打开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()
?
答案 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调用。