触发COM事件时Lock()/ Unlock()调用的目的是什么?

时间:2014-04-04 13:37:54

标签: c++ events visual-c++ com atl

在ATL COM服务器中触发事件的典型代码就是这样(从this question复制并修剪一下):

HRESULT Fire_MessageTrigger()
{
    HRESULT hr = S_OK;
    T * pThis = static_cast<T *>(this);
    int count = m_vec.GetSize();

    for (int i = 0; i < count; i++)
    {
        pThis->Lock(); // I'm asking about this...
        CComPtr<IUnknown> punkConnection = m_vec.GetAt(i);
        pThis->Unlock(); // and this

        IDispatch* pConnection = static_cast<IDispatch *>(punkConnection.p);
        if (pConnection == 0)
            continue;

        DISPPARAMS params = { NULL, NULL, 0, 0 };
        hr = pConnection->Invoke(2, IID_NULL, LOCALE_USER_DEFAULT,
            DISPATCH_METHOD, &params, 0, NULL, NULL);
    }
    return hr;
}

Lock()Unlock()来电的目的是什么?

1 个答案:

答案 0 :(得分:5)

围绕m_vec成员变量(接收器列表)的线程安全措施。

您可以在并发线程上执行以下操作:

template <class T, const IID* piid, class CDV>
STDMETHODIMP IConnectionPointImpl<T, piid, CDV>::Advise(
    _Inout_ IUnknown* pUnkSink,
    _Out_ DWORD* pdwCookie)
{
// ...
        pT->Lock();
        *pdwCookie = m_vec.Add(p); // <<--- Modifying m_vec here
        hRes = (*pdwCookie != NULL) ? S_OK : CONNECT_E_ADVISELIMIT;
        pT->Unlock();

你的下一个问题是他们在访问m_vec.GetSize();时没有锁定几行的原因。

这是他们这样做的方式:他们可以在并发向量访问的情况下错过单个事件传递,只要它不会创建内存损坏或其他未定义的行为。毕竟,订阅呼叫可能会在稍后发生并且无论如何都会错过此事件。这同样适用于事件取消订阅的情况(Unadvise)。

此处的优先级是尽可能少地锁定,然后尽快解锁。请注意,接收器调用在解锁时发生,接收器列表可以进行修改,同时我们正在遍历调用它们的接收器列表。

UPD。 Visual Studio 2008始终生成Lock / Unlock对。更新版本也是如此。从 “\ VC \ VCWizards \ CodeWiz \ ATL \ ImplementInterface \ HTML \ 1033 \ default.htm”文件:

strProxyMethod +=
    "\t\tfor (int iConnection = 0; iConnection < cConnections; iConnection++)\r\n"+
    "\t\t{\r\n"+
    "\t\t\tpThis->Lock();\r\n"+
    "\t\t\tCComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);\r\n"+
    "\t\t\tpThis->Unlock();\r\n\r\n";

早期的VS版本可以生成无锁代码,只有在从VS IDE手动重新生成代理类时才会更新代码。