Okazało się, że shared_ptr i słabe_ptr, długie z listą, wykonały pracę, której potrzebowałem. Mój problem polegał na tym, że kilku klientów chciało wchodzić w interakcje z wewnętrznymi danymi hosta. Zazwyczaj host aktualizuje dane samodzielnie, jednak jeśli klient tego zażąda, musi przerwać aktualizację, dopóki żaden klient nie uzyska dostępu do danych hosta. W tym samym czasie klient może poprosić o wyłączny dostęp, aby żaden inny klient ani host nie mógł modyfikować danych hosta.
Jak to zrobiłem, stworzyłem strukturę:
struct UpdateLock
{
typedef std::shared_ptr< UpdateLock > ptr;
};
Każdy klient miałby członka takiego:
UpdateLock::ptr m_myLock;
Wtedy host miałby element slaby_ptr na wyłączność i listę slabych_ptrs dla blokad niewyłącznych:
std::weak_ptr< UpdateLock > m_exclusiveLock;
std::list< std::weak_ptr< UpdateLock > > m_locks;
Dostępna jest funkcja umożliwiająca blokowanie i inna funkcja sprawdzająca, czy host jest zablokowany:
UpdateLock::ptr LockUpdate( bool exclusive );
bool IsUpdateLocked( bool exclusive ) const;
Testuję blokady w LockUpdate, IsUpdateLocked i okresowo w ramach procedury aktualizacji hosta. Testowanie blokady jest tak proste, jak sprawdzenie, czy słaby_ptr wygasł i usunięcie dowolnego wygasłego z listy m_locks (robię to tylko podczas aktualizacji hosta). Mogę sprawdzić, czy lista jest pusta; w tym samym czasie otrzymuję automatyczne odblokowanie, gdy klient resetuje shared_ptr, na którym się trzyma, co również ma miejsce, gdy klient zostaje automatycznie zniszczony.
Ogólny efekt jest taki, że ponieważ klienci rzadko potrzebują wyłączności (zwykle zarezerwowanej tylko dla dodatków i usunięć), w większości przypadków żądanie LockUpdate (false), to znaczy niewyłączne, kończy się sukcesem, o ile (! M_exclusiveLock). LockUpdate (true), żądanie wyłączności, kończy się sukcesem tylko wtedy, gdy zarówno (! M_exclusiveLock), jak i (m_locks.empty ()).
Można dodać kolejkę, aby złagodzić blokady między wyłącznymi i niewyłącznymi blokadami, jednak do tej pory nie miałem żadnych kolizji, więc zamierzam poczekać, aż to się stanie, aby dodać rozwiązanie (głównie dlatego, że mam rzeczywisty warunek testu).
Jak dotąd działa to dobrze na moje potrzeby; Mogę sobie wyobrazić potrzebę rozszerzenia tego i pewne problemy, które mogą pojawić się podczas rozszerzonego użytkowania, jednak było to szybkie do wdrożenia i wymagało bardzo mało niestandardowego kodu.