Возможно ли иметь два экземпляра COM STA одного и того же компонента?

У меня была проблема, обнаруженная здесь в другом потоке, мне нужно для доступа к компоненту COM, который является STA. Я запускаю его на двухъядерном компьютере, процесс, использующий этот компонент, достигает только 50% процессорного времени. К сожалению, владельцы сказали, что не могут поменять компонент на MTA, потому что компонент представляет собой гибридную систему, скомпилированную в Matlab, ядром которой является C.

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

Я рассматриваю возможность запуска двух процессов моей службы на одном компьютере для достижения 100% загрузки процессора. Это не очень хорошее решение, в основном потому, что эти серверы будут установлены за пределами нашей инфраструктуры.


person Victor Rodrigues    schedule 10.11.2008    source источник
comment
Можете ли вы уточнить, что вы подразумеваете под недоступностью? Нет ничего принципиально плохого в наличии двух экземпляров компонента STA в одном процессе, поэтому нам потребуется больше данных, чтобы помочь вам отследить проблему.   -  person JaredPar    schedule 10.11.2008
comment
WAG здесь (поэтому не ответ) ... Как насчет создания двух потоков STA, каждый из которых владеет одним экземпляром компонента COM?   -  person    schedule 10.11.2008


Ответы (3)


К вопросу о нескольких компонентах STA

Можно иметь два экземпляра одного и того же компонента STA COM и получать к ним доступ из C#. Единственное, что может помешать вам в таком сценарии, — это сам объект, если он реализован как объект-одиночка.

Однако, если оба экземпляра находятся в одном потоке STA, активный вызов в одном из экземпляров заблокирует любые другие вызовы в этом потоке. Таким образом, если вы хотите, чтобы эти два экземпляра работали параллельно, вам нужно, чтобы они находились в отдельных потоках STA. На всякий случай я бы создал оба экземпляра в фоновых потоках. Это должно предотвратить блокировку вашего пользовательского интерфейса.

По теме STA и MTA для внешнего компонента

Я не уверен, почему компонент, находящийся в C, не позволяет ему быть объектом MTA. Быть MTA просто означает, что объект должен внутренне синхронизировать свой доступ к состоянию и код управления между несколькими потоками.

ВНИМАНИЕ: Уродливый хак! :-) Если вы хотите немного поэкспериментировать, вы можете зайти в реестр и изменить модель потоковой передачи внешнего компонента с Apartment на Free, просто чтобы убедиться, что ваш код будет работать правильно. с МТА. Однако их компонент, вероятно, сломается, поскольку они, вероятно, не написали потокобезопасный код, полагаясь на COM для их защиты.

Сделайте заметку на видном месте, чтобы отменить это изменение позже, чтобы не получить систему, в которой их код не работает, и вы потратили бесчисленные часы на погоню за призраками. :-)

person Franci Penov    schedule 10.11.2008
comment
На самом деле у меня будет около 500 процессов каждые 30 минут, каждый из которых открывает поток, два потока одновременно. - person Victor Rodrigues; 10.11.2008
comment
Я понял, что вы сказали, но как я могу поделиться двумя экземплярами COM со всеми этими потоками, если я создам эти экземпляры для их фоновых потоков? Мне нужно их инициализировать и разместить где-то на видном месте глобально, а не в их фоновых потоках. - person Victor Rodrigues; 10.11.2008
comment
Различные процессы не будут сериализованы между собой. Но вашему приложению не обязательно иметь более двух потоков, так как любые вызовы экземпляра компонента в одном и том же процессе будут сериализованы, поэтому вы привязаны к количеству экземпляров, которые у вас есть. - person Franci Penov; 10.11.2008
comment
После того как вы создали экземпляр в фоновом потоке, вы можете получить к нему доступ из любого потока. Взаимодействие COM и .Net позаботится о переключении контекста потока вызова. Однако вам нужно поддерживать фоновый поток и запускать в нем цикл обработки сообщений (STA работает с сообщениями). - person Franci Penov; 10.11.2008

Франси Пернов,

Я пробовал работать с двумя потоками и инициализировать экземпляры com в контексте каждого потока, но ошибка та же самая: (Исключение из HRESULT: 0x80004005 (E_FAIL))

Я сохраняю и извлекаю экземпляр через CallContext GetData и SetData.

person Victor Rodrigues    schedule 11.11.2008

Попробуйте зарегистрировать второй класс, используя ту же DLL. Учтите, что вам может понадобиться отдельная копия DLL с другим именем, чтобы быть в полной безопасности.

Просто помните, что класс STA COM (и, возможно, его DLL) не считается потокобезопасным для многопоточности, и вы ничего не можете сделать с этим внешним по отношению к классу COM.

person orcmid    schedule 10.11.2008
comment
Нет ничего плохого в использовании компонентов STA в многопоточных программах. COM будет правильно сериализовать вызовы STA-объекта из разных потоков (при условии, что указатели интерфейса были правильно распределены между потоками). - person Franci Penov; 10.11.2008
comment
Наличие второй копии DLL совершенно не поможет. Вызов CoCreateinstanceEx (который новый оператор в C# будет делать за кулисами) с другим CLSID завершится ошибкой, так как фабрика классов объектов не сработает. Вызов с исходным CLSID загрузит исходную DLL. - person Franci Penov; 10.11.2008
comment
Он хочет, чтобы ему не приходилось выполнять сериализованные вызовы, чтобы он мог свободно обрабатывать два из них. Я так понимаю, ему нужно сделать новый CLSID для второго. Разве он не может сделать это, не переделывая второй код COM DLL? - person orcmid; 10.11.2008
comment
Вы не можете изменить CLSID для COM-компонента, не изменив реализацию DllGetClassObject. Большинство реализаций DllGetClassObject по умолчанию сравнивают CLSID для CoCIEx() со списком известных CLSID в DLL, чтобы вернуть правильную фабрику классов. - person Franci Penov; 11.11.2008
comment
Да, я вижу это в DllGetClassObject. Еще один способ попробовать это — построить DLL для другого CLSID и использовать ее DllGetObject LoadLibrary с другой DLL (не CoGetObject) и вызвать эту запись DllGetClassObject с правильным CLSID. Ни идеально, ни прекрасно. Стоит попробовать в крайнем случае.? - person orcmid; 11.11.2008