Будет ли COM-сортировка (когда-либо) необходимой для объекта с ThreadingModel Both?

Это вызвано Другой вопрос.

В частности, у меня есть в процессе COM-класс, который определен в Реестр CLSID как имеющий ThreadingModel из Both.

Наш процесс активирует этот объект через CoCreateInstance (не CoCreateInstanceEx, если это даже имеет значение для in-proc dll сервера)

Учитывая модель потоков Bothи учитывая правила, перечисленные в документы:

Threading model of server | Apartment server is run in
------------------------------------------------------
Both                      | Same apartment as client

и учитывая то, что Ганс пишет в другом ответе:

… Маршалинг происходит, когда клиентский вызов должен быть сделан на
другая нить. … может произойти, когда ThreadingModel указан в
элемент comClass требует этого. Другими словами, когда COM-объект
был создан в одном потоке, но вызывается в другом, а сервер
не потокобезопасен.

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

Это правильно, даже если клиентский процесс работает как STA?

4

Решение

Да, может быть маршалинг.

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

Терминология COM может быть очень запутанной. Когда вы ссылаетесь на «клиента» в этом случае, вы на самом деле ссылаетесь на поток, а не на все приложение (как это подразумевается).

Both просто означает, что потоковая модель сервера соответствует клиенту, который его создает. То есть, когда вы создаете экземпляр своего класса, он принимает модель потока, в котором он был создан. Поскольку вы создаете экземпляр сервера в STA, ваш сервер будет использовать STA, то есть он может быть вызван только в потоке, который его создал; если другой поток попытается вызвать его, он будет маршалировать в поток, в котором он был создан.

6

Другие решения

Я не могу удержаться от публикации этого сообщения, хотя это не прямой ответ на вопрос.

Есть замечательная статья MSKB из золотых веков COM: ИНФОРМАЦИЯ: Описание и работа моделей потоков OLE. Все еще там, и имеет всю необходимую информацию. Дело в том, что вы не должны беспокоиться о том, есть ли маршалинг или нет, если вы следуете правилам. Просто зарегистрируйте свой объект как ThreadingModel=BothОбъедините Свободнопоточного Маршалера с CoCreateFreeThreadedMarshalerи будет сделано. COM при необходимости сделает маршалинг наилучшим образом. В зависимости от модели квартиры клиента, код клиента может получить прямой указатель на ваш интерфейс, если он также следует правилам.

Любой «чужой» интерфейс, который вы можете получить при вызове метода вашего интерфейса, будет действительным в рамках вызова, потому что вы остаетесь в том же потоке. Если вам не нужно хранить его, это все, что имеет значение.

Однако, если вам необходимо кэшировать «чужой» интерфейс, правильный способ сделать это — сохранить его, используя CoMarshalInterThreadInterfaceInStream/CoGetInterfaceAndReleaseStream:

Чтобы сохранить это:

  • Войдите в критический раздел;
  • вызов CoMarshalInterThreadInterfaceInStream и хранить IStream указатель в поле члена;
  • Покинуть критическую секцию;

Чтобы получить его

  • Войдите в критический раздел;
  • вызов CoGetInterfaceAndReleaseStream восстановить интерфейс
  • вызов CoMarshalInterThreadInterfaceInStream и сохранить его снова как IStream для любого будущего использования
  • Покинуть критическую секцию;
  • Использовать интерфейс в объеме текущего звонка

Чтобы выпустить это:

  • Когда вам больше не нужно хранить его, просто отпустите сохраненный IStream (внутри критического раздела).

Если «чужой» объект тоже является свободным потоком, и вещи происходят внутри одного и того же процесса, вы, вероятно, будете иметь дело с прямым указателем интерфейса после того, как CoGetInterfaceAndReleaseStream, Однако вы не должны делать никаких предположений, и вам действительно не нужно знать, является ли объект, с которым вы имеете дело, исходным объектом или прокси-сервером COM-маршала.

Это может быть немного оптимизировано с помощью CoMarshalInterface ж / MSHLFLAGS_TABLESTRONG / CoUnmarshalInterface / IStream::Seek(0, 0) / CoReleaseMarshalData вместо CoGetInterfaceAndReleaseStream/CoGetInterfaceAndReleaseStream, чтобы разархивировать один и тот же интерфейс столько раз, сколько необходимо, не освобождая поток.

Возможны более сложные (и, возможно, более эффективные) сценарии кэширования с использованием Thread Local Storage. Тем не менее, я считаю, что это будет излишним. Я не сделал никакого выбора времени, но я думаю, что накладные расходы CoMarshalInterThreadInterfaceInStream/CoGetInterfaceAndReleaseStreamдействительно низко.

Тем не менее, если вам нужно поддерживать состояние, которое хранит любые ресурсы или объекты, которые могут потребовать сродства потока, кроме вышеупомянутых COM-интерфейсов, вы не следует пометить свой объект как ThreadingModel=Both или агрегировать FTM.

3

Да, сортировка по-прежнему возможна. Пара примеров:

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

  2. объект создается из потока STA и помещается в квартиру STA, принадлежащую этому потоку, а затем его указатель передается в другой поток STA или поток MTA. В обоих случаях эти потоки могут получить доступ к объекту только через маршаллинг.

Фактически, вы можете ожидать, что не будет сортировки только в следующих двух случаях:

  1. объект создается из потока MTA, а затем доступен только для потоков MTA — как того, который создал объект, так и всех других потоков MTA того же процесса.
  2. объект создается из потока STA и затем доступен только этому потоку

и во всех других случаях начнется сортировка.

2

ThreadingModel = Оба просто означает, что автор COM-сервера может дать гарантию, что его код является поточно-ориентированным, но не может дать такую ​​же гарантию, что Другой код, который он не писал, будет вызываться потокобезопасным способом. Наиболее распространенный случай получения такого внешнего кода — обратные вызовы, точки подключения — наиболее распространенный пример (обычно называемый «событиями» во время выполнения клиента).

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

1
По вопросам рекламы [email protected]