2013-04-08 7 views
12

Ho una classe con thread da cui vorrei acquisire occasionalmente un puntatore una variabile di istanza. Vorrei che questo accesso fosse protetto da un mutex in modo che il thread fosse bloccato dall'accesso a questa risorsa fino a quando il client non ha finito con il suo puntatore.Come posso creare un puntatore intelligente che blocca e sblocca un mutex?

Il mio approccio iniziale a questo è di restituire una coppia di oggetti: uno un puntatore alla risorsa e uno un shared_ptr a un oggetto di blocco sul mutex. Questo shared_ptr contiene l'unico riferimento all'oggetto lock in modo che il mutex debba essere sbloccato quando esce dall'ambito. Qualcosa del genere:

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

Questa soluzione non è l'ideale perché richiede al client di trattenere l'intera coppia di oggetti. Comportamento come questo rompe il filo di sicurezza:

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

Inoltre, la mia propria implementazione di questo è blocco critico e sto avendo difficoltà a determinare il motivo per cui, così ci possono essere altre cose che non va.

Quello che mi piacerebbe avere è un shared_ptr che contiene il blocco come variabile di istanza, collegandolo con i mezzi per accedere alla risorsa. Questo sembra qualcosa che dovrebbe avere un modello di progettazione stabilito, ma avendo fatto qualche ricerca sono sorpreso di trovare abbastanza difficile da trovare.

Le mie domande sono:

  • C'è un'implementazione comune di questo modello?
  • Ci sono problemi con il mettere un mutex in un shared_ptr che sto trascurando che impedisca questo pattern di essere diffuso?
  • C'è una buona ragione per non implementare la mia classe shared_ptr per implementare questo pattern?

(NB sto lavorando su una base di codice che utilizza Qt, ma purtroppo non è possibile utilizzare spinta in questo caso. Tuttavia, le risposte che coinvolgono spinta sono ancora di interesse generale.)

+0

Entrambe le risposte del riv e Jonanthan Wakely sono interessanti e degne di essere seguite. Vado con Riz solo perché è bello avere un codice completo di modifica collaborativa in una risposta. –

risposta

7

non sono sicuro se ci sono eventuali implementazioni standard, ma dal momento che mi piace ingegnose Re-implementazione per nessun motivo, ecco una versione che dovrebbe funzionare (a patto che non vuole essere in grado di copiare tali puntatori):

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 questo dovrebbe farlo - forse disabilitare l'assegnazione e aggiungere un costruttore di mosse? – stijn

+0

Certo, puoi disabilitare il compito spostando la sezione 'operator =' in privato, ma dovrebbe essere sicuro finché i mutex supportano i blocchi annidati. Aggiunto costruttore di mosse. – riv

+0

Risolti alcuni errori di battitura; è possibile disabilitare l'assegnazione con la parola chiave '= delete', ma non è supportata dal mio Visual Studio. – riv

3

che stai descrivendo una variante del modello EXECUTE AROUND POINTER, descritta da Kevlin Henney nel numero Executing Around Sequences.

Ho un'implementazione di prototipo allo exec_around.h ma non posso garantire che funzioni correttamente in tutti i casi in quanto è un lavoro in corso. Include una funzione mutex_around che crea un oggetto e lo avvolge in un puntatore intelligente che blocca e sblocca un mutex quando vi si accede.