Подсчет ссылок COM — потокобезопасность

#c #multithreading #com #reference

#c #многопоточность #com #ссылка

Вопрос:

Если у меня есть COM-объект, требуется ли, чтобы методы AddRef () и Release () были потокобезопасными, т. Е. чтобы я должен был использовать атомарные операции для подсчета ссылок?

Комментарии:

1. То же правило применяется и к другим методам. STA COM-объекту не нужно заботиться о безопасности потоков, MTA COM-объекту — должен (InterlockedXXX — самый простой подход).

Ответ №1:

Да, если вы используете модель aparement со свободным потоком, используйте InterlockedIncrement() и InterlockedDecrement() для обработки количества ссылок.

Комментарии:

1. Использование InterlockedIncrement () и т.д. — это общая идиома, и она потокобезопасна.

Ответ №2:

Я думаю, что ответ отрицательный. Это не требуется. Если вы хотите, чтобы ваш COM-объект был потокобезопасным, тогда они должны быть потокобезопасными. В противном случае они не должны быть.

Например. если вы посмотрите здесь: В правилах компонентной объектной модели это не упоминается как требование. Также в кулинарной книге COM-программиста (Создание COM-компонента) можно увидеть образец объекта без потокобезопасного подсчета ссылок.

Фрагмент кода Microsoft:

 ULONG COutside::AddRef (void)
{
    return    m_cRef;
}
  

На практике большинство реализаций делали бы это, потому что в противном случае COM-объекты не были бы потокобезопасными. Если вы знаете, что объект будет использоваться только в одном потоке, я считаю, что это разрешенная оптимизация. Не все COM-объекты потокобезопасны, я работал с несколькими, которые таковыми не являлись.

Чтобы разобраться с тем фактом, что COM-объекты могут быть потокобезопасными, а могут и не быть, COM предлагает разные «квартиры», в которых создаются COM-объекты. В однопоточном хранилище только один поток может получить доступ к объектам внутри этого хранилища, тогда как в многопоточном хранилище объекты могут совместно использоваться несколькими потоками. Цитирую из Понимания и использования моделей потоковой обработки COM:

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

Комментарии:

1. Ваш ответ, хотя и не совсем бесполезный, опасно неполон. Вы должны хотя бы упомянуть о возможности свободного доступа к потокам (о чем на связанной странице, по причинам, недоступным моему пониманию, также не упоминается)

2. Тем не менее, это правильно. Программист должен не использовать потокобезопасные объекты в неправильном разделе. Опасно ли это? ДА. Но я не сделал это таким образом. Я добавлю примечания об этом к своему ответу.

3. Честно говоря: я думаю, что большинство COM-объектов не являются свободнопоточными (фактически, в те дни у меня сложилось отчетливое впечатление, что гуру MS делали из этого величайший технологический трюк века по созданию многопоточного COM-объекта); Культура COM заключалась / остается в том, чтобы скрывать сложность потоков и автоматически маршалировать через границы квартиры. Однако, поскольку вопрос был открытым и конкретно упоминал threadsafety, вам следует хотя бы упомянуть сценарий со свободными потоками 🙂 Что вы сейчас и делаете, подсказка шляпы

4. @sehe: У меня тоже был такой опыт. Я ответил на вопрос буквально, требуется ли в COM, чтобы они были потокобезопасными. Возможно, мне нужно было добавить больше фона — все еще привыкаю к этому SO 🙂

Ответ №3:

Ага. Это обязательно. COM — это простой двоичный стандарт, и если вы используете бесплатные поточные модули, вы получите действительно бесплатные поточные доступа

Ответ №4:

Это будет зависеть от используемой вами потоковой модели и типа объекта. Пожалуйста, ознакомьтесь с описанием _ATL_*_THREADED макросов. Эти макросы влияют на потокобезопасность AddRef()/Release() «обычных» классов и фабрик.

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

Вот как вы выбираете правильный макрос (и это объясняет, AddRef()/Release() должен ли он быть потокобезопасным).

Если у всех классов одного сервера не указана потоковая модель (Main STA), то нет возможности одновременного доступа ни к объектам, ни к фабрикам, и все они могут иметь режим non-threadsafe AddRef()/Release() , и вы получите это, указав _ATL_SINGLE_THREADED макрос.

В противном случае, если хотя бы в одном классе указана модель «Apartment», вам нужна потокобезопасность AddRef()/Release() для фабрики этого объекта, но все еще может быть не потокобезопасность AddRef()/Release() в самом объекте, и вы получите это, указав _ATL_APARTMENT_THREADED макрос. Этот макрос сделает все фабрики потокобезопасными AddRef()/Release() , а все объекты — не-потокобезопасными AddRef()/Release() .

Наконец, если хотя бы в одном классе указана модель потоков «Оба» или «Свободно», вам нужно AddRef()/Release() быть потокобезопасным как в этом классе, так и на заводе, и вам нужно либо указать _ATL_FREE_THREADED , либо просто не указывать ничего из вышеперечисленного — этот «самый жесткий» эффект макроса будет включен по умолчанию. Таким образом, конфигурация по умолчанию для COM-объектов, созданных с использованием ATL, должна быть потокобезопасной AddRef()/Release() для всех объектов — как обслуживаемых объектов, так и фабрик.

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