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.
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. –
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! –
@ChrisBeck grazie per il tuo tentativo di spiegare. Vi chiedo di affrontare il problema e la sua soluzione in una "risposta". –