0

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

4

1 回答 1

1

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

自从我上次自动化 Matlab 以来已经十年了,所以请原谅下面的生锈。您收到 CoInitialize 错误表明 engOpen 调用包装了底层 COM 调用。不幸的是,这会让您不知不觉地接触到 COM 的蠕虫罐头。我猜你是对的,engOpen 包含对 CoInitialize 的调用,它初始化当前线程上的 COM 库。要从线程访问 COM 对象,必须始终在调用 COM 之前在该线程上调用 CoInitialize (除了一个允许的 COM 函数,我忘了是哪个。)

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

[我花了大约十年的时间在 COM 上,它充满了熊陷阱。你可以花几个星期阅读微软试图用 .Net 隐藏/杀死的技术,但最好现在就走简单的(单线程)路径并忘记它。]

更新 恐怕您的编辑已使您陷入 COM 线程模型的泥潭。COINIT_MULTITHREADED 有效地告诉 COM 您将处理线程的所有细微差别,这几乎肯定不是您想要做的。COM 使用多个(上次我注意到它是三个)线程模型进行操作,您传递给 CoInitializeEx 的参数声明您希望使用哪些模型。

如果以下内容略有偏差,请向所有人道歉。

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

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

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

COM 在许多方面都令人惊叹,但其错综复杂却令人头疼。感谢您永远不必使用分布式 COM,这真的很讨厌!

更新 2 我对 COM 线程模型的古老记忆是错误的。此 MSDN 页面指出,每个公寓都有一个带有 COINIT_APARTMENTTHREADED 的线程。COM 对象可以使用相同的接口指针从创建它们的单元中的所有线程访问。对于 COINIT_APARTMENTTHREADED,这意味着只是创建对象的线程。在 COINIT_MULTITHREADED 中,这将是多线程单元中的所有线程,但是(1)如果您使用 engOpen,您将无法选择在哪个线程上创建 Matlab 引擎,并且(2)尝试调用您没有的 COM 对象从多线程单元写入是有风险的。原来的 OLE 线程模型只允许来自主 GUI 线程的 COM 调用,顺便说一句。

于 2013-10-08T14:37:10.837 回答