2013-04-08 40 views
12

Mam klasy wątków, z których chciałbym od czasu do czasu uzyskać wskaźnik zmiennej instancji. Chciałbym, aby ten dostęp był chroniony przez muteks, aby wątek był zablokowany przed dostępem do tego zasobu, dopóki klient nie zakończy pracy z jego wskaźnikiem.Jak utworzyć inteligentny wskaźnik, który blokuje i odblokowuje muteks?

Moim początkowym podejściem do tego jest zwrócenie pary obiektów: jednego wskaźnika do zasobu i jednego shared_ptr do obiektu blokady na muteksie. Ten shared_ptr zawiera jedyne odniesienie do obiektu blokady, więc muteks powinien zostać odblokowany, gdy wykracza poza zakres. Coś takiego:

void A::getResource() 
{ 
    Lock* lock = new Lock(&mMutex); 
    return pair<Resource*, shared_ptr<Lock> >(
     &mResource, 
     shared_ptr<Lock>(lock)); 
} 

To rozwiązanie jest mniej niż idealne, ponieważ wymaga, aby klient trzymał się całej pary obiektów. Zachowanie w ten sposób łamie bezpieczeństwo wątku:

Resource* r = a.getResource().first; 

Ponadto, moja własna implementacja to deadlocking i mam trudności w określeniu, dlaczego, więc nie może być inne rzeczy złego.

Co chciałbym mieć, to shared_ptr, która zawiera blokadę jako zmienną instancji, wiążąc ją ze środkami dostępu do zasobu. To wydaje się być czymś, co powinno mieć ustalony wzorzec projektowy, ale po przeprowadzeniu pewnych badań jestem zaskoczony, że napotykam go dość trudno.

Moje pytania są następujące:

  • Czy istnieje wspólna realizacja tego wzoru?
  • Czy są problemy z umieszczeniem muteksu wewnątrz pliku shared_ptr, którego nie pamiętam, co uniemożliwia rozpowszechnienie tego wzorca?
  • Czy istnieje dobry powód, aby nie implementować mojej własnej klasy shared_ptr do implementacji tego wzorca?

(NB pracuję na kodzie, który używa Qt, ale niestety nie można używać impuls w tym przypadku. Jednak odpowiedzi obejmujące impuls są nadal w ogólnym interesie.)

+0

Obie odpowiedzi od riv i Jonanthan Wakely są interesujące i godne naśladowania. Idę z Rizem tylko dlatego, że miło jest mieć jakiś kompletny wspólnie edytowany kod w odpowiedzi. –

Odpowiedz

7

Nie jestem pewien, czy istnieje są jakieś standardowe implementacje, ale ponieważ lubię ponownego wdrażania rzeczy bez powodu tu jest wersja, która powinna działać (zakładając, że nie chcą, aby móc kopiować takie wskaźniki):

template<class T> 
class locking_ptr 
{ 
public: 
    locking_ptr(T* ptr, mutex* lock) 
    : m_ptr(ptr) 
    , m_mutex(lock) 
    { 
    m_mutex->lock(); 
    } 
    ~locking_ptr() 
    { 
    if (m_mutex) 
     m_mutex->unlock(); 
    } 
    locking_ptr(locking_ptr<T>&& ptr) 
    : m_ptr(ptr.m_ptr) 
    , m_mutex(ptr.m_mutex) 
    { 
    ptr.m_ptr = nullptr; 
    ptr.m_mutex = nullptr; 
    } 

    T* operator ->() 
    { 
    return m_ptr; 
    } 
    T const* operator ->() const 
    { 
    return m_ptr; 
    } 
private: 
    // disallow copy/assignment 
    locking_ptr(locking_ptr<T> const& ptr) 
    { 
    } 
    locking_ptr& operator = (locking_ptr<T> const& ptr) 
    { 
    return *this; 
    } 
    T* m_ptr; 
    mutex* m_mutex; // whatever implementation you use 
}; 
+0

+1 to powinno zrobić - może wyłączyć przypisanie i dodać konstruktor ruchu? – stijn

+0

Oczywiście, możesz wyłączyć przypisanie, przenosząc 'operator =' do sekcji prywatnej, ale powinno być bezpieczne dopóki muteksy obsługują zagnieżdżone blokady. Dodano konstruktor ruchu. – riv

+0

Naprawiono kilka literówek; możesz wyłączyć przypisanie słowem kluczowym '= delete', ale nie jest ono obsługiwane przez moje Visual Studio. – riv

3

ty opisujesz odmiana wzoru EXECUTE AROUND POINTER, opisana przez Kevlina Henneya w Executing Around Sequences.

Mam prototypową implementację pod numerem exec_around.h, ale nie mogę zagwarantować, że działa poprawnie we wszystkich przypadkach, ponieważ jest to praca w toku. Zawiera on funkcję mutex_around, która tworzy obiekt i owija go w inteligentny wskaźnik, który blokuje i odblokowuje muteks podczas uzyskiwania dostępu.