Многопоточное приложение C ++ с использованием Matlab Engine

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() открывает COM-канал для Matlab. Когда двигатель открыт, поток падает в режиме ожидания.

Затем, в другом потоке, я делаю это:

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 ); исключение first-chance на engEvalString( MatlabEngine, cPathToScript ); который, кажется, говорит мне, что сервер Matlab COM недоступен (но механизм Matlab все еще запущен и работает).

Когда я помещаю все в один поток, он отлично работает, но это не тот дизайн, который я имел в виду.

Я считаю, что в документации по двигателю Matlab отсутствует информация о двигателе + COM. Любая идея, как иметь инициализацию и вызовы функций в отдельных потоках?

Благодаря !

EDIT после ответа 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 исключения из первого The application called an interface that was marshalled for a different thread :) У меня так весело сегодня утром! :)

Итак, CoMarshalInterface()?

Теги:
multithreading
com
matlab-engine

1 ответ

1
Лучший ответ

CoInitialize следует вызывать из каждого потока, где вы используете COM-объект, а не только основной поток.

Прошло уже десять лет с тех пор, как я в последний раз автоматизировал Matlab, поэтому извиняюсь за ржавость в дальнейшем. То, что вы получили ошибку CoInitialize, указывает на то, что вызов engOpen завершает вызовы COM-вызовов. К сожалению, это разоблачает вас в банке червей, которая является COM. Я думаю, вы правы и что engOpen включает вызов CoInitialize, который инициализирует библиотеку COM в текущем потоке. Чтобы получить доступ к COM-объектам из потока, CoInitialize должен всегда вызываться в этом потоке перед любыми вызовами COM (кроме одной разрешенной функции COM, я забыл, какой из них).

Мой совет - изолировать все ваши вызовы Matlab в один поток. Если вы это сделаете, вам не придется делать явный вызов CoInitialize, и вы избегаете каких-либо более поздних многопоточных проблем COM. Вы можете заставить свою программу работать сегодня, вызывая CoInitialize во втором потоке, но однажды вы будете укушены другой проблемой COM.

[Я провел около десяти лет локтями на COM, и он полон медвежьих ловушек. Вы можете потратить несколько недель на чтение технологии, которую Microsoft пыталась скрыть/убить с помощью.Net, но лучше просто взять простой (однопоточный) путь и забыть об этом.]

Обновление. Боюсь, что ваше редактирование привело вас в болото COM-потоков. COINIT_MULTITHREADED эффективно сообщает COM, что вы позаботитесь обо всех маленьких нюансах потоковой передачи, что почти наверняка не то, что вы хотите сделать. COM работает с несколькими (в прошлый раз, когда я обращал внимание, что это было три), модели потоков и параметр, который вы передаете в CoInitializeEx, объявляет, какую из этих моделей вы хотите использовать.

Извиняюсь перед всем, если что следует ниже.

Если вы укажете COINIT_MULTITHREADED, вам нужно знать, что COM-объект, который вы вызываете, является потокобезопасным или выполняет соответствующую блокировку (и сортировку интерфейсов и данных между потоками) самостоятельно.

COINIT_APARTMENTTHREADED, что, вероятно, является тем, что использует engOpen, поскольку по моему опыту он наиболее распространен, позволяет библиотеке COM обрабатывать многопоточность для вас. Библиотека может, например, создавать объекты прокси и заглушки для посредничества вызовов по потокам (или границ процесса, что и произойдет при вызове в Matlab).

engOpen создал прокси-объект Matlab в основном потоке. Этот прокси-объект может быть вызван из потока, где он был создан, и, если я правильно помню, из любого другого потока в "квартире" (где CoInitializeEx был вызван с COINIT_APARTMENTTHREADED.) Вы пытались вызвать через прокси из потока в другой модели потоков, COM-библиотека заметила и выпустила указанную вами ошибку.

Во многом COM поражает, но тонкости - это боль в заднице. Будьте благодарны, что вам вряд ли придется использовать Distributed COM, что действительно противно!

Обновление 2 Моя древняя память о COM-потоковых моделях неверна. На этой странице MSDN указано, что для каждой квартиры есть один поток с COINIT_APARTMENTTHREADED. Объекты COM могут быть доступны с использованием того же указателя интерфейса из всех потоков в квартире, где они были созданы. Для COINIT_APARTMENTTHREADED это означает только поток, в котором был создан объект. В COINIT_MULTITHREADED это будут все потоки в многопоточной квартире, но (1) вы не сможете выбрать, в каком потоке будет создан механизм Matlab, если вы используете engOpen и (2) пытаетесь вызвать COM-объект, писать из многопоточной квартиры рискованно. Первоначальная модель потоковой передачи OLE разрешала только COM-вызовы из основного потока GUI, BTW.

  • 0
    Дело в том, что я нигде не называю CoInitialize. Угадайте, это должно быть выполнено engOpen? (что является функцией механизма Matlab) Более того, я никогда не использовал CoInitialize, какие-либо предположения, как я мог бы сделать это?
  • 0
    РобХ, пожалуйста, взгляните на мою правку выше. Думаю, я закончу тем, что перенесу все в ту же ветку, как вы предлагали
Показать ещё 6 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню