2014-04-04 12 views
5

Typowy kawałek kodu wypalania zdarzeń w serwerze ATL COM idzie tak (skopiowane z this question i przycięty kawałek):Jaki jest cel blokowania()/Unlock() podczas uruchamiania zdarzeń COM?

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; 
} 

Co jest celem Lock() i Unlock() połączeń?

Odpowiedz

5

Jest to miara bezpieczeństwa gwintów wokół zmiennej składowej m_vec (lista zlewów).

Można mieć następującą wykonywane na jednoczesnym wątku:

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(); 

Twoje następne pytanie byłoby, dlaczego nie są one blokowania kilka linijek wyżej wokół dostępu m_vec.GetSize();.

Oto sposób, w jaki to zrobili: nie są w stanie przegapić pojedynczego zdarzenia w przypadku równoczesnego dostępu do wektorów, o ile nie powoduje to uszkodzenia pamięci lub w inny sposób niezdefiniowanego zachowania. W końcu subskrybowanie połączenia może nastąpić chwilę później i tak czy inaczej przegapi to wydarzenie. To samo dotyczy przypadku anulowania subskrypcji ze zdarzeń (Unadvise).

Priorytetem jest zablokowanie tak rzadko, jak to możliwe, a następnie odblokowanie tak szybko, jak to możliwe. Zauważ, że wywołanie zlewu następuje podczas odblokowywania, a lista zlewów może zająć modyfikację, podczas gdy my sprawdzamy listę zlewów, które je wywołują.

UPD. Visual Studio 2008 zawsze generuje parę: Lock/Unlock. Więc czy nowsze wersje. Od "\ VC \ VCWizards \ CodeWiz \ ATL \ ImplementInterface \ HTML \ 1033 \ default.htm" file:

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"; 

Wcześniejsze wersje VS można wygenerować kod blokady wolne i kod jest aktualizowany tylko wtedy, gdy ponownie ręcznie generuj klasę proxy z VS IDE.

+0

Okay, widziałem również podobny kod, który nie blokuje() '/' Unlock() '. Czy byłoby słuszne stwierdzenie, że jeśli serwer powinien obsługiwać współbieżność, wówczas cały kod wystrzeliwujący zdarzenia powinien "Zablokować()"/"Odblokować()" bieżący obiekt? – sharptooth

+0

Tu chodzi o bezpieczeństwo nici. Czasami potrzebujesz blokady, czasami nie. Przez cały czas jesteś zainteresowany utrzymaniem stanu zablokowanego w minimalnym możliwym czasie. Zauważ, że z bazą STA ('CComSingleThreadModel') te połączenia powodują opróżnienie metody i wygenerowanie kodu. –

+0

Załóżmy, że mam dziesięć klas COM z tym samym modelem wątkowania innym niż główna STA i każda obsługuje niektóre zdarzenia i wszystkie są implementowane za pomocą 'IConnectionPointImpl'. Czy będę potrzebował takiego blokowania w każdym z nich? – sharptooth