Вопросы о многопоточности COM и STA / MTA - PullRequest
2 голосов
/ 06 июня 2011

Привет, я новичок в COM. Я хочу проверить COM DLL в режимах STA и MTA. Мой первый вопрос: возможно ли, что COM-объект поддерживает STA и MTA?

Теперь я представляю фрагмент кода STA ниже:

// this is the main thread
m_IFoo;
CoInitializeEx(STA); // initialize COM in main thread
CreateInstance(m_IFoo);
m_IFoo->Bar();

CreateThread(ThreadA);
// start ThreadA

// this is secondary thread
ThreadA()
{
    CoInitializeEx(STA);
    m_IFoo->Buz(); // call m_IFoo's method directly
}

Будет ли этот код работать? Я пропускаю какие-то фундаментальные вещи? Я знаю, что основной поток нуждается в цикле сообщений окна, чтобы позволить вызовам из других потоков выполняться. Должен ли я что-нибудь с этим сделать?

Теперь я перехожу к тестированию MTA. Если я просто заменю «STA» на «MTA» в приведенном выше коде, это будет работать?

Другой вопрос: поскольку поток с графическим интерфейсом должен быть STA, я не могу инициализировать и тестировать MTA в потоке графического интерфейса?

Заранее спасибо и извините за то, что я наивен на COM и потоке.

Ответы [ 2 ]

3 голосов
/ 07 июня 2011

Ваш код не является допустимым COM, потому что вы передаете указатель непосредственно от одной STA к другой, что COM не позволяет.

В COM указатели интерфейса имеют «сходство с квартирой», их можно использовать только внутри квартиры. Чтобы передать указатель из одной STA в другую или между STA и MTA, необходимо «маршалировать» указатель на безопасное представление, которое затем не сортируется принимающим потоком.

Самый простой способ сделать это - использовать Global Interface Table ; вы регистрируете интерфейс с ним в одном потоке и возвращаете DWORD, который затем используете в другом потоке, чтобы получить версию интерфейса, которую может использовать другой поток.

Если оба потока MTA, вы можете избежать этого. В то время как STA один на поток - каждый поток STA имеет свою собственную структуру - MTA совместно используется всеми потоками MTA. Это означает, что потоки MTA могут свободно передавать COM-указатели между собой. (Но они все равно должны маршалировать при передаче указателей на потоки STA или из них.)

Вообще говоря, вы не меняете код между STA или MTA, вы обычно решаете это один раз в самом начале. Если у потока есть пользовательский интерфейс, то он нуждается в цикле сообщений и обычно является STA. Если пользовательского интерфейса нет, вы можете решить использовать MTA. Но как только вы примете это решение и напишете свой код, редко можно будет перейти к другому позже, так как выбор одного или другого имеет другие требования и предположения, которые влияют на код; перейдите с STA на MTA или наоборот, и вам придется внимательно просмотреть код и посмотреть, нужно ли менять такие вещи, как назначение указателей.

2 голосов
/ 06 июня 2011

Возможность переключения с «MTA» на «STA», и последствия такого переключения будут зависеть от того, как объект зарегистрирован в системном реестре. Чтобы объект «поддерживал» оба случая без маршалинга, ему необходимо установить ThreadingModel на Both.

Пожалуйста, посмотрите этот отличный ответ - Both означает "либо Free, либо Apartment в зависимости от того, как вызывающий абонент инициализирует COM". Это именно то, что вы хотите.

Что касается использования режима "STA" - да, протекторный объект, которому принадлежит, должен будет запустить цикл сообщений, вызвав в цикле GetMessage(), TranslateMesage() и DispatchMessage(). В любом случае методы объектов не будут вызываться напрямую из второго потока - они будут проходить через прокси. Пожалуйста, смотрите эту очень хорошую статью для подробного объяснения.

...