2016-01-01 32 views
5

Da qui: (!) Logic error in my defined Mutex class and the way I use it in producer consumer program - pthreadsPerché passare i riferimenti alla classe Mutex non è un buon progetto?

il modo in cui si passa intorno riferimenti a vostra classe mutex è chiaramente in cerca di guai, sfida ogni tipo di incapsulamento.

Perché questo è un problema? Dovrei essere passato per valore e poi scritto il costruttore di copie?

Quali danni possono mancare in questo caso per l'incapsulamento? Come dovrei incapsulare cosa?

Inoltre, Why is passing references to the Mutex class not a good design?

Passare un riferimento al blocco è una cattiva idea - non si "usa" il blocco, è solo acquisire e poi dare indietro. Spostarlo rende difficile monitorare l'utilizzo della risorsa (critica). Passare un riferimento a una variabile mutex piuttosto che a un lock non è forse così grave, ma rende ancora più difficile sapere quali parti del programma potrebbero bloccarsi, quindi è qualcosa da evitare.

Si prega di spiegare in un linguaggio semplice con esempi - perché i riferimenti di passaggio è una cattiva idea?

+1

Bene, di solito si usano i mutex in modo estremamente accurato. Quando si blocca un mutex, si desidera che il blocco abbia una durata molto breve e scelta con cura in modo da non causare un deadlock. Passare un riferimento alla serratura è una cattiva idea: non si "usa" la serratura, si acquisisce e si restituisce. Spostarlo rende difficile monitorare l'utilizzo della risorsa (critica). Passare un riferimento a una variabile mutex piuttosto che a un lock non è forse così grave, ma rende ancora più difficile sapere quali parti del programma potrebbero bloccarsi, quindi è qualcosa da evitare. Di solito non hai mai bisogno di farlo. –

+0

Anche se passi in modo specifico "pthread_mutex &" alla tua classe mutex, voglio dire che è un po 'brutto ... per esempio, puoi ottenere un riferimento ciondolante se non stai attento, e per due, sei legato all'attuazione del pthread. Presumibilmente, il punto della tua lezione è quello di nascondere i dettagli di implementazione, ma qui stai palesemente trapelandoli! –

+0

@ChrisBeck grazie per il tuo tentativo di spiegare. Vi chiedo di affrontare il problema e la sua soluzione in una "risposta". –

risposta

3

Vorrei distillarlo per separare le domande: 1. Quando è opportuno passare qualsiasi oggetto per riferimento? 2. Quando è opportuno condividere un mutex?

  1. Il modo in cui si passa un oggetto come argomento riflette come ci si aspetta di condividere la vita dell'oggetto tra il chiamante e il chiamato. Se si passa per riferimento, si deve presupporre che il callee utilizzerà l'oggetto solo per la durata della chiamata o, se il riferimento è memorizzato dal destinatario, la durata della vita del chiamato è inferiore al riferimento. Se si tratta di oggetti allocati dinamicamente, è probabile che si stiano utilizzando indicatori intelligenti, che (tra le altre cose) consentono di comunicare in modo più esplicito le proprie intenzioni (vedere Herb Sutter's treatment di questo argomento).

  2. La condivisione di un mutex dovrebbe essere evitata. Questo è vero se si passa per riferimento o per qualsiasi altro mezzo. Condividendo un mutex, un oggetto si lascia condizionare internamente da un'entità esterna. Ciò viola l'incapsulamento di base ed è una ragione sufficiente. (Vedi qualsiasi testo sulla programmazione orientata agli oggetti per le virtù dell'incapsulamento). Una vera conseguenza nella condivisione del mutex è la probabilità di stallo.

    prendere questo semplice esempio:

    • A possiede mutex
    • Un azioni mutex con B
    • B ottiene il blocco prima funzione di chiamata su una funzione
    • Un tenta di ottenere il blocco
    • Deadlock ..

Dal punto di vista del design, perché vorresti condividere un mutex? Un mutex protegge una risorsa a cui è possibile accedere da più thread. Quel mutex dovrebbe essere nascosto (incapsulato) all'interno di una classe che controlla quella risorsa. Un mutex è solo un modo in cui questa classe può proteggere la risorsa; è un dettaglio di implementazione di cui solo la classe dovrebbe essere a conoscenza. Invece, condividi l'istanza della classe che controlla la risorsa e consenti di garantire la sicurezza dei thread in se stessa in qualsiasi modo desideri.

3

Penso che sia una brutta astrazione e un incapsulamento errato. a mutex di solito è predefinito costruito con i costruttori di copie cancellati, avendo più oggetti mutex che si riferiscono allo stesso oggetto logico è soggetto ad errori, cioè può portare a deadlock e altre condizioni di competizione perché il programmatore o il lettore può assumere che sono entità differenti.

Inoltre, specificando quale mutex interno si sta utilizzando si espongono i dettagli di implementazione dei thread interrompendo così l'astrazione della classe Mutex. Se si sta usando un pthread_mutex_t, molto probabilmente si useranno i thread del kernel (pthreads).

L'incapsulamento si interrompe anche perché il Mutex non è una singola entità incapsulata ma piuttosto dispersa in numerosi riferimenti (eventualmente penzolanti).

Se si vuole incapsulare un pthread_mutex_t in una classe si dovrebbe fare come in modo

class Mutex { 
public: 
    void lock(); 
    void unlock(); 

    // Default and move constructors are good! 
    // You can store a mutex in STL containers with these 
    Mutex(); 
    Mutex(Mutex&&); 
    ~Mutex(); 

    // These can lead to deadlocks! 
    Mutex(const Mutex&) = delete; 
    Mutex& operator= (const Mutex&) = delete; 
    Mutex& operator= (Mutex&&) = delete; 

private: 
    pthread_mutex_t internal_mutex; 
}; 

oggetti mutex sono destinate ad essere condivise in un ambito condiviso dichiarato nei file di implementazione piuttosto che ha dichiarato a livello locale e passato in giro come funzioni di riferimento. In teoria, passerai solo argomenti al costruttore di thread di cui hai bisogno. Passare attorno a riferimenti a oggetti dichiarati in un ambito allo stesso "livello" della funzione in questione (esecuzione del thread in questo caso) di solito porta a errori nel codice. Cosa succede se l'ambito in cui viene dichiarato il mutex non esiste più? Il distruttore dello mutex invaliderà l'implementazione interna del mutex? Cosa succede se il mutex si fa strada verso un intero altro modulo passando per il passato e quel modulo inizia i suoi thread e pensa che il mutex non bloccherà mai, questo può portare a brutte situazioni di stallo.

Anche un caso in cui si desidera utilizzare il costruttore di mutex move è in un modello di fabbrica mutex, se si desidera creare un nuovo mutex si effettua una chiamata di funzione e tale funzione restituirebbe un mutex che si otterrebbe aggiungi alla tua perdita di mutex o passa a un thread che lo richiede tramite una sorta di dati condivisi (la lista di cui sopra sarebbe una buona idea per questi dati condivisi).Ottenere un modello di fabbrica di mutex di questo tipo, tuttavia, può essere piuttosto complicato in quanto è necessario bloccare l'accesso all'elenco comune di mutex. Dovrebbe essere una cosa divertente da provare!

Se l'intenzione dell'autore era di evitare l'ambito globale, dichiararlo una volta in un file di implementazione come oggetto statico dovrebbe essere un'astrazione sufficiente.

+0

grazie mille. Ho preso tempo per capire la tua risposta. Quello che ho capito è che se usiamo il costruttore di mosse invece di passare per riferimento, non ci dovremo preoccupare dell'eliminazione dell'oggetto locale esterno. È corretto? C'è qualcosa di diverso da questo che volevi trasmettere attraverso la tua risposta? –

+0

Sì, volevo trasmettere altre cose attraverso la mia risposta. Prima di tutto i costruttori di movimento sono a portata di mano con i mutex, non perché vorreste passarli da soli, ma nel caso in cui dite un vettore di mutex e ridimensionate il vettore per aggiungere nuovi mutex, il mutex deve essere spostato da una matrice all'altro. Il secondo punto principale che volevo trasmettere era. Nella maggior parte dei casi un oggetto mutex dovrebbe essere globale. Il terzo punto principale che volevo trasmettere era che passare un mutex in giro per riferimento è chiedere una condizione di gara – Curious

+0

Grazie! Posso pubblicare la taglia adesso hhahaha. Ma torniamo al terzo punto. Ho personalmente scritto questo codice nel mio primo progetto concorrente, quindi parlo per esperienza. Ho avuto il seguente codice impostato. In un thread se c'era un accesso a un blocco di dischi a cui non era associato un mutex, ho usato un pattern factory per darmi un nuovo oggetto mutex e quindi passarlo al costruttore del thread come riferimento. Ho quindi proceduto a staccare il filo. Questo quindi ha causato una corsa nella distruzione del mutex e non ero felice :(Spero che questo abbia risolto il tuo dubbio bene :) – Curious