Я знаю, что API-интерфейсы блокировки используются внутри методов AddRef и Release COM-объекта для увеличения / уменьшения потока подсчета ссылок. Но я пытаюсь понять, что здесь достаточно использовать API-интерфейсы блокировки или нам нужны какие-то другие объекты синхронизации, например, Мьютекс. Пока что весь пример кода, который я видел, использовал только API-интерфейсы блокировки.
сценарий— Предположим, я реализовал методы AddRef и Release объекта сообщения следующим образом. Допустим, поток A обращается к объекту сообщения, поэтому счетчик m_lRef равен 1. Как только это сделано с потоком использования объекта сообщения A вызывает метод Release
Метод внутреннего выпуска —
В строке № 9 — m_lRef = 1
В строке № 10 — m_lRef = 0, lRef = 0
Поток A приостанавливается в строке # 10, а другой поток B обращается к тому же сообщению, поэтому он вызывает метод AddRef, который установит значение m_lRef равным 1 в строке # 3. Теперь поток B приостановлен, а поток A возобновляется в строке # 11 — m_lRef = 1, lRef = 0. Поток A удалит объект. Теперь, если поток B пытается получить доступ к тому же объекту; Крах неизбежен.
Мой вопрос — Мой сценарий действителен? если m_lRef = 1, то в идеале никакой другой поток не должен ожидать доступа к объекту. Но чтобы предотвратить сбой в таком неожиданном сценарии, разве мы не должны защищать весь метод выпуска с помощью мьютекса или CS?
1. STDMETHODIMP_(ULONG) CMapiMsg::AddRef()
2. {
3. LONG lRef = InterlockedIncrement(&m_lRef);
4. return lRef;
5. }
6.
7. STDMETHODIMP_(ULONG) CMapiMsg::Release()
8. {
9. LONG lRef = InterlockedDecrement(&m_lRef);
10. if(0 == lRef)
11. {
12. delete this;
13. }
14. return lRef;
15. }
Логика идет не так:
m_lRef = 0
Поток A приостанавливается на строке № 10 и другая тема B имеет доступ к тому же сообщению поэтому он вызывает метод AddRef, который установит значение m_lRef равным 1 в строке # 3. Теперь тема B приостановлена, а тема A возобновлена
Если поток B содержит действительную ссылку COM на этот объект, то один поток A не может юридически уменьшить счетчик до нуля. Поток B все еще содержит что-то, поэтому счетчик ссылок должен быть как минимум один, когда поток A освобождает все, что у него есть …
Если поток B только делает свое первое приращение, он должен в любом случае получить указатель на интерфейс откуда-то, что предполагает выдающуюся ссылку. Если что-то передает указатель без строгой ссылки, то это проблема вызывающего абонента, а не проблема подсчета.
В целом, Interlocked API эффективен и достаточен для подсчета ссылок.
Других решений пока нет …