Я открываю механизм 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()
?
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.