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

标签 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 channel 。 一旦引擎打开,线程就会进入等待事件模式。

然后,在另一个线程中,我这样做:

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

我在 engEvalString( MatlabEngine, cPathToScript ); 上得到一个 CoInitialize has not been called 第一次机会异常,这似乎告诉我 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() ?

最佳答案

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

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

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

[我在 COM 上花费了大约十年的时间,它充满了熊市陷阱。您可以花几周时间阅读 Microsoft 试图用 .Net 隐藏/扼杀的技术,但最好现在就采取简单的(单线程)路径并忘记它。]

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

如果以下内容略有偏差,我们深表歉意。

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

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

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

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

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

关于c++ - 使用 Matlab 引擎的多线程 C++ 应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19250493/

相关文章:

c++ - 从基类指向成员的指针类型

c++ - 用.cpp 和.c 制作?

c# - 来自 fft 结果的功率谱密度 C#

MATLAB 同步代码

c++ - 字节冲突

c++ - 强制转换的结果是右值吗?

ios - 更新标签使 UIImageview 不可见的线程问题

根据 CPU 和 RAM 使用情况调整线程池的 Java Executor

java - JVM 在调用 Thread.run() 之前在后台做什么?或者为什么 Thread.run() 没有暴露给用户?

matlab - fiddle 图 Matlab