Поиск

Модели потоков в COM

В начале программирования в СОМ большинство людей мало знает (или не знают вообще) о моделях потоков и окружений в СОМ. Не набравшись опыта, они не понимают, что за модель свободных потоков приходится расплачиваться значительным снижением производительности, когда клиентский поток в однопоточном окружении (single-threaded apartment, STA) используется для создания объекта в многопоточном окружении (multithreaded apartment, MTA). Кроме того, начинающие программисты в СОМ часто не знают о безопасности потоков и о том, чем им угрожает одновременное обращение нескольких потоков к их компонентам СОМ.

Прежде чем вызвать объект СОМ, поток должен объявить свою принадлежность к определенному окружению, указывая, что он может входить в STA или МТА. Клиентские потоки STA вызывают Colnitiali-ze(NULL) или Со Initialize Ex(О, COINIT_APARTMENTTHREADED), чтобы войти в STA, а потоки МТА вызывают Со Initialize Ех(О, COINIT_MULTI-THREADED) для входа в МТА. Сходным образом в мире .NET у вас есть выбор: вы можете позволить вызывающему потоку в управляемом пространстве объявить свою принадлежность к некоторому окружению. Вызывающий поток в управляемом приложении выбирает МТА как свое место обитания по умолчанию. Это аналогично тому, как если бы вызывающий поток инициализировался с параметрами CoInitializeEx(0, COINIT_MULTITHREADED). Но подумайте об издержках и потерях производительности, которые будут иметь место, если вызывающим потоком будет классический СОМ-компонент STA, разработанный для отдельного потока многопоточного процесса. Несовместимые окружения также приведут к издержкам в связи с дополнительными парами "про-кси/заглушка", за что определенно придется расплачиваться производительностью.

В связи с этим вы можете изменить выбор окружения по умолчанию для управляемого потока в приложении .NET с помощью свойства Apart-mentState класса System. Threading. Thread. Свойство ApartmentState принимает одно из значений:

  • МТА — многопоточное окружение;
  • STA — однопоточное окружение;
  • Unknown — эквивалентно поведению МТА по умолчанию.

До осуществления любых вызовов объекта СОМ вам также нужно задать свойство ApartmentState для вызывающего потока. Заметьте: после создания объекта СОМ изменить свойство ApartmentState нельзя. Поэтому имеет смысл установить свойство потока ApartmentState в программе как можно раньше. Вот как это сделать:

// Установить свойство клиентского потока ApartmentState для входа в STA.

Thread.CurrentThread.ApartmentState = ApartmentState.STA;

// Создать объект COM посредством Interop. MySTA obj STA = new MySTAQ; objSTA.MyMethodQ

Подведем итоги

Последнее, о чем я хочу сказать, это как различные механизмы работы с унаследованным кодом (PInvoke, небезопасный код и COM Interop) вписываются в общую схему .NET. В этой главе вы узнали следующее. Ш Как пользоваться PInvoke и некоторыми атрибутами, чтобы облегчить решение задач, связанных с преобразованием различных типов данных, включая пользовательские данные, когда применяются стандартные С-подобные вызовы функций.

  • Что касается небезопасного кода, вы узнали, как отказываться от преимуществ управляемого кода в приложениях на С#, оказавшись в ситуации, когда вам требуется больший контроль над памятью. Эти сценарии могут включать случаи, когда вам требуется ручное манипулирование памятью ради повышения эффективности или при перемещении в приложение на С# блоков кода, к преобразованию которого в управляемый код вы просто еще не готовы.
  • Относительно СОМ вы увидели, как задействовать классические компоненты СОМ в приложениях .NET и как COM Interop обеспечивает бесшовное многократное использование существующих компонентов СОМ из управляемого кода. Далее мы бегло ознакомились со способами вызова вашего компонента СОМ с помощью как раннего, так и позднего связывания, а также со способами проверки типов в период выполнения. В завершение вы увидели, как управляемые потоки объявляют о своей принадлежности к тому или иному окружению при вызове компонентов СОМ.

Теперь у вас, как у разработчиков, использующих один из этих механизмов для работы с неуправляемым кодом, может возникнуть вопрос. Продолжать ли применять эти методологии или сразу перейти в мир .NET путем написания всех компонентов и кода прикладной логики в виде управляемых компонентов с помощью такого языка .NET, как С#? Ответ зависит от конкретных обстоятельств.

Если у вас тонны унаследованного кода, будь это С-подобные функции в DLL, код, непосредственно манипулирующий памятью, компоненты СОМ или комбинация всех трех видов кода, за одну ночь вы, по всей вероятности, не сможете конвертировать все это богатство. Тогда имеет смысл задействовать различные механизмы .NET для работы с унаследованным кодом. Если же вы пишете код прикладной логики "с нуля", я бы искренне советовал вам писать его в виде управляемых компонентов на таком языке, как С#. При этом вы покончите с потерями производительности, неизбежно возникающими при переходе границ между управляемым и неуправляемым мирами.