【问题标题】:What's the purpose of Lock()/Unlock() calls when firing COM events?触发 COM 事件时 Lock()/Unlock() 调用的目的是什么?
【发布时间】:2014-05-16 20:43:57
【问题描述】:

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() 调用的目的是什么?

【问题讨论】:

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


    【解决方案1】:

    这是围绕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) 的情况。

    这里的优先级是尽可能少地锁定,然后尽快解锁。请注意,sink 调用是在解锁时发生的,并且 sink list 可以在我们迭代调用它们的 sink 列表时进行修改。

    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 手动重新生成代理类时才会更新。

    【讨论】:

    • 好的,我也看到了类似的代码,但不是Lock()/Unlock()。如果服务器应该支持并发,那么所有事件触发代码都应该Lock()/Unlock()当前对象是正确的吗?
    • 一切都与线程安全有关。有时您需要锁定,有时则不需要。在任何时候,您都有兴趣在尽可能短的时间内保持锁定状态。另请注意,对于 STA 基 (CComSingleThreadModel),这些调用解析为空方法并且不生成任何代码。
    • 假设我有十个 COM 类,除了 Main STA 之外,它们具有相同的线程模型,每个类都支持一些事件,它们都使用IConnectionPointImpl 实现。我需要对所有这些都进行这种锁定吗?
    • 也许你想为你的最后一个问题提供一个更具体的例子。因为这些Lock/Unlocks 保护对m_vec 成员的访问(这个实例!​​无论您在项目中有多少类和实例),然后在另一种情况下,我们可能会谈论完全不同的事情。
    • 嗯,我明白了。假设我有十个班级......(如上面的评论) - 只在其中一些班级而不是其他班级有这种锁定是否有意义?
    猜你喜欢
    • 2011-03-21
    • 2018-10-12
    • 2020-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-19
    相关资源
    最近更新 更多