оптимізація додатків

Об'єктна модель програмних компонентів (Component Object Model, COM) проектувалася з метою дати можливість створювати компоненти на будь-якій мові / платформі, що володіє підтримкою цієї моделі, і використовувати їх в будь-якій мові / платформі (другом), так само володіє підтримкою COM. Платформа .NET не виняток і дозволяє легко використовувати сторонні COM-об'єкти і експортувати типи .NET у вигляді COM-об'єктів.

Суть організації взаємодій з COM-об'єктами та ж, що і при використанні механізму P / Invoke: ви оголошуєте кероване уявлення COM-об'єкта, а Виконавча CLR створює об'єкт-обгортку, який реалізує маршалинга. Існує два різновиди обгорток: обгортка, що викликається середовищем виконання (Runtime Callable Wrapper, RCW). яка дозволяє керованому коду використовувати COM-об'єкти:

оптимізація додатків

і обгортка, що викликається COM-об'єктами (COM Callable Wrapper, CCW). дає можливість COM-об'єктів викликати керований код:

оптимізація додатків

Сторонні COM-об'єкти часто поставляються разом з основною складанням взаємодій (Primary Interop Assembly, PIA). містить визначення, схвалені виробником, підписаної і встановлюється в глобальний кеш збірок (Global Assembly Cache, GAC). В іншому випадку можна скористатися інструментом tlbimp.exe. є частиною Windows SDK, який автоматично генерує збірку взаємодій, спираючись на інформацію, що міститься в бібліотеці типів.

При взаємодіях з COM-об'єктами повторно використовується інфраструктура маршалинга параметрів механізму P / Invoke, але з іншими умовчаннями (наприклад, за замовчуванням рядка перетворюються в тип BSTR), тому всі поради, що були дані в попередній статті щодо механізму P / Invoke, також застосовні і тут.

Модель COM має власні проблеми продуктивності, зумовлені характерними особливостями COM, такими як многопоточная модель підрозділів і неузгодженість між природою COM, заснованої на підрахунку посилань, і моделлю збірки сміття в .NET.

Управління життєвим циклом

Отримуючи посилання на COM-об'єкт в .NET, ви фактично отримуєте посилання на об'єкт-обгортку RCW. Обгортка RCW завжди зберігає єдину посилання на COM-об'єкт і для кожного COM-об'єкта створюється тільки один екземпляр об'єкта-обгортки RCW. Обгортка RCW підтримує власний лічильник посилань, не пов'язаний з лічильником посилань COM-об'єкта. Значення цього лічильника посилань зазвичай дорівнює 1, але може бути більше, за участю в маршалинга більшого числа інтерфейсів або коли до одного і того ж інтерфейсу звертається кілька потоків виконання.

Оскільки збирач сміття в .NET запускається в непередбачувані моменти часу і не знає про блоках некерованою пам'яті, виділених при створенні обгорток RCW для COM-об'єктів, він не може прискорити складання сміття, внаслідок чого обсяг займаної пам'яті може виявитися дуже великим.

При необхідності можна викликати метод Marshal.ReleaseComObject (). щоб явно звільнити об'єкт. Кожен виклик зменшує лічильник посилань в RCW і коли він досягне нуля, автоматично зменшується лічильник посилань у відповідному COM-об'єкт (точно так само, як при виклику фііалізатора RCW). Після виклику методу Marshal.ReleaseComObject () не можна використовувати обгортку RCW.

Якщо після виклику лічильник посилань RCW може виявитися більше нуля, метод Marshal.ReleaseComObject () слід викликати в циклі, поки він не поверне нульове значення. Найкраще викликати Marshal.ReleaseComObject () всередині блоку finally, щоб гарантувати звільнення COM-об'єкта, навіть якщо десь між створенням його примірника і звільненням виникне виключення.

Маршалинга через кордони підрозділів

Модель COM реалізує власні механізми синхронізації потоків виконання для підтримки викликів між різними потоками, які можуть використовуватися навіть при роботі з об'єктами, з самого початку не призначеними для використання в багатопотокової середовищі. Ці механізми можуть знижувати продуктивність при неправильному їх застосуванні. Хоча ця проблема не має прямого відношення до взаємодій з COM-об'єктами з .NET, проте, її варто обговорити, тому що з нею часто стикаються на практиці, ймовірно тому, що розробники, які звикли до типових прийомів синхронізації в .NET можуть не знати, що конкретно відбувається під покровом COM-об'єктів.

Модель COM пов'язує об'єкти і потоки виконання з підрозділами (apartments). службовцями межами, через які модель COM виконує виклики. Усього є кілька типів підрозділів:

Однопоточні підрозділу (Single-Threaded Apartment, STA)

У кожному підрозділі є єдиний потік виконання, але може бути будь-яку кількість об'єктів. В процесі може бути кілька підрозділів STA.

Багатопотокові підрозділу (Multi-Threaded Apartment, МТА)

У кожному підрозділі може бути будь-яка кількість потоків виконання і об'єктів, але в процесі може бути лише один підрозділ МТА. Цей тип використовується в .NET за замовчуванням.

Потоконезавісімие підрозділу (Neutral-Threaded Apartment, NTA)

Містять об'єкти, але не потоки. В процесі може бути лише один підрозділ NTA.

Зв'язування потоку виконання з підрозділом відбувається при виклику CoInitialize або CoInitializeEx для ініціалізації COM-об'єкта в цьому потоці. Функція CoInitialize пов'язує потік з новим підрозділом STA, а функція CoInitializeEx дозволяє вказати тип підрозділу, STA або МТА.

В .NET вам не доведеться викликати ці функції безпосередньо, замість цього досить додати атрибут STAThread або MTAThread до точки входу в потік (методу Main). При бажанні можна також викликати метод Thread.SetApartmentState () або встановити значення у властивості Thread.ApartmentState перед запуском потоку виконання. Якщо не вказано інше. NET инициализирует потоки (включаючи головний потік додатку) як належать підрозділу МТА.

Зв'язування COM-об'єктів з підрозділами виконується, виходячи з параметра ThreadingModel в реєстрі, який може мати такі значення:

Single - об'єкт за замовчуванням поміщається в підрозділ STA.

Apartment - об'єкт повинен бути поміщений в будь-який підрозділ STA, і тільки потоку з цього підрозділу буде дозволено викликати об'єкт безпосередньо. Інші екземпляри можуть поміщатися в інші підрозділи STA.

Free - об'єкт поміщається в підрозділ МТА. Цей об'єкт може викликатися безпосередньо і одночасно з будь-якої кількості потоків в підрозділі МТА. Об'єкт повинен забезпечувати підтримку використання в багатопотокової середовищі.

Both - об'єкт поміщається в підрозділ створила його програми (STA або МТА). По суті, після створення він стає STA- або МТА-подібним об'єктом.

Neutral - об'єкт поміщається в потоконезавісімое підрозділ і не вимагає маршалинга. Це - найбільш ефективний режим.

На малюнку нижче зображено схема взаємин між підрозділами, потоками і об'єктами:

оптимізація додатків

При спробі створити об'єкт з моделлю підтримки потоків, не сумісної з моделлю потоків в поточному підрозділі, ви отримаєте покажчик на інтерфейс, який в дійсності вказує на проксі-об'єкт. Якщо буде потрібно передати інтерфейс COM-об'єкта іншому потоку, що належить іншому підрозділу, покажчик на інтерфейс повинен передаватися не безпосередньо, а через механізм маршалинга. Інфраструктура COM поверне відповідний проксі-об'єкт.

В процесі маршалинга виклик функції (включаючи параметри) перетвориться в повідомлення, яке буде надіслано в чергу приймає підрозділу STA. Для об'єктів STA чергу реалізується як приховане вікно, віконна процедура якого приймає повідомлення і передає COM-об'єкту за допомогою заглушки (stub). При такому підході COM-об'єкти в підрозділі STA COM завжди викликаються в одному і тому ж потоці виконання, завдяки чому забезпечується безпека при роботі в багатопотоковому оточенні.

Якщо викликає підрозділ не сумісно з підрозділом COM-об'єкта, відбувається перемикання потоку і виконується маршалинга параметра між потоками.

Щоб уникнути падіння продуктивності через маршалинга між потоками, намагайтеся забезпечити відповідність між підрозділом COM-об'єкта і підрозділом створює його потоку. Створення та використання попередніх COM-об'єкти STA в потоках з підрозділу STA, а COM об'єкти з підрозділу МТА - в потоках МТА. COM-об'єкти, помічені, як підтримують обидва типи підрозділів, можуть вільно використовуватися з будь-яких потоків виконання без зайвих накладних витрат.

Виклик об'єктів STA з ASP.NET

За замовчуванням середу ASP.NET виконує сторінки в потоках МТА. Якщо з цих сторінок викликаються об'єкти, що знаходяться в підрозділах STA, в справу вступає механізм маршалинга. Якщо основна маса використовуваних об'єктів належить підрозділам STA, це призведе до деградації продуктивності. Цю проблему можна ліквідувати, позначивши сторінки атрибутом AspCompat. як показано нижче:

Зверніть увагу, що конструктори сторінок все ще виконуються в потоці виконання МТА, тому створення об'єктів STA слід виконувати в обробниках подій Page_Load і Page_Init.

Імпорт бібліотек типів і Code Access Security

Механізм Code Access Security виконує ті ж перевірки безпеки, що і P / Invoke. Ви можете додавати ключ / unsafe при виклику утиліти tlbimp.exe, яка буде додавати атрибут SuppressUnmanagedCodeSecurityAttribute до згенерованих типам. Використовуйте цю можливість тільки в системах, що користуються у вас безумовною довірою, так як вона може породжувати проблеми безпеки.

До виходу версії .NET Framework 4.0 доводилося разом з додатком поширювати збірки взаємодій або основні збірки взаємодій (Primary Interop Assemblies, PIA). Ці збірки зазвичай виходили дуже великими (навіть в порівнянні з кодом, що використовують їх) і як правило не входять до інсталяційний комплект COM-компонентів; замість цього їх необхідно встановлювати окремо, тому що самі вони не потрібні для роботи самих COM-об'єктів. Інша причина, чому збірки PIA не включаються в установчі комплекти, полягає в тому, що вони встановлюються в глобальний кеш збірок (GAC). Це вводить залежність від .NET Framework в інакше повністю незалежні додатки.

Починаючи з версії .NET Framework 4.0, компілятори C # і VB.NET можуть перевірити, які COM-інтерфейси і методи використовуються в коді, і скопіювати і вбудувати в зухвалу збірку тільки дійсно необхідні визначення, зменшуючи розмір коду і позбавляючи від необхідності поширювати бібліотеки PIA. У Microsoft ця особливість була названа NoPIA. Вона діє як щодо основних збірок взаємодій, так і по відношенню до збірок взаємодій в цілому.

Складання PIA володіють однією важливою особливістю, яка називається еквівалентністю типів. Так як вони мають суворе іменування і поміщаються в глобальний кеш збірок, різні керовані компоненти можуть обмінюватися обгортками RCW і з точки зору .NET вони матимуть еквівалентні типи. Навпаки, збірки взаємодій, згенеровані за допомогою tlbimp.exe, не володіють такою особливістю, так як кожен компонент в цьому випадку отримає власну, окрему від інших, складання взаємодій. З появою підтримки особливості NoPIA відпала необхідність в строгому іменуванні збірок, і в Microsoft було запропоновано рішення, що дозволяє інтерпретувати обгортки RCW з інших збірок, як належать того ж типу, якщо інтерфейси мають однаковий ідентифікатор GUID.

Щоб включити підтримку NoPIA, виберіть пункт Properties в контекстному меню Visual Studio після клацання правою кнопкою миші на збірці взаємодій в розділі References, і встановіть параметр Embed Interop Types (Впроваджувати типивзаємодій) в значення True:

оптимізація додатків

винятки

Більшість методів COM-інтерфейсів повідомляють про успіх чи невдачу, повертаючи значення типу HRESULT. Негативні значення HRESULT (з встановленим старшим бітом) повідомляють про помилку, а нуль (S_OK) або позитивні значення - про успіх. Крім того, COM-об'єкт може повертати додаткову інформацію про помилку при виконанні функції SetErrorInfo, передаючи об'єкт IErrorInfo, створений викликом CreateErrorInfo.

При виклику COM-методу через механізм взаємодій з COM, заглушка маршалера перетворює значення HRESULT в кероване виняток, згідно самому значенню HRESULT і даними, що містяться в об'єкті IErrorInfo. Оскільки порушення виключення є досить дорогою операцією, функції COM-об'єкта, які часто зазнають невдачі, можуть негативно позначатися на продуктивності. Ви можете придушити автоматичне перетворення винятків, позначивши методи атрибутом PreserveSigAttribute. При цьому вам доведеться змінити керовану сигнатуру, як повертає значення типу int, в результаті чого параметр retval стане параметром out.