En général, vous ne devez pas détruire un objet tant que vous ne savez pas qu'aucun autre thread ne l'utilise. Période.
Considérons ce scénario, en fonction de votre 'train de la pensée':
- Discussion A: Récupère l'objet X référence
- Discussion A: Objet de verrouillage X
- Discussion B: Récupère l'objet X référence
- discussion B: Bloc sur l'objet verrou X
- discussion A: Déverrouillez objet X
- Sujet B: Verouiller X; déverrouiller l'objet X; détruire un objet X
Considérons maintenant ce qui se passe si le timing est légèrement différent:
- Discussion A: Récupère l'objet référence X
- Discussion B: Récupère l'objet X référence
- Discussion B: verrouillage objet X; déverrouiller l'objet X; détruire un objet X
- Discussion A: Verouiller X - Crash
En bref, la destruction de l'objet doit être synchronisé quelque part autre que l'objet lui-même. Une option courante consiste à utiliser le comptage des références. Le thread A se verrouille sur la référence d'objet elle-même, empêchant la suppression de la référence et la destruction de l'objet, jusqu'à ce qu'il parvienne à incrémenter le nombre de références (en conservant l'objet en vie). Ensuite, le thread B efface simplement la référence et décrémente le compte de référence. Vous ne pouvez pas prédire quel thread appellera réellement le destructeur, mais il sera sûr de toute façon.
Le modèle de comptage de référence peut être implémenté facilement en utilisant boost::shared_ptr
ou std::shared_ptr
; le destructeur ne s'exécutera que si tous les threads ont été détruits (ou faits pour pointer ailleurs), donc au moment de la destruction, vous savez que le seul pointeur sur l'objet restant est le pointeur this
du destructeur lui-même.
Notez que lorsque vous utilisez shared_ptr, il est important d'empêcher la modification de la référence d'objet d'origine jusqu'à ce que vous puissiez en capturer une copie. Par exemple:
std::shared_ptr<SomeObject> objref;
Mutex objlock;
void ok1() {
objlock.lock();
objref->dosomething(); // ok; reference is locked
objlock.unlock();
}
void ok2() {
std::shared_ptr<SomeObject> localref;
objlock.lock();
localref = objref;
objlock.unlock();
localref->dosomething(); // ok; local reference
}
void notok1() {
objref->dosomething(); // not ok; reference may be modified
}
void notok2() {
std::shared_ptr<SomeObject> localref = objref; // not ok; objref may be modified
localref->dosomething();
}
Notez que simultanément lit sur un shared_ptr
est sûr, vous pouvez choisir d'utiliser un verrou en lecture-écriture s'il est logique pour votre application.
Pourquoi détruisez-vous l'objet alors qu'il pourrait encore y avoir des threads qui y accèdent? –
Legacy code, mon ami –
Mes sympathies, alors :-) –