Question

Je parcourais un code hérité et j'ai trouvé l'extrait suivant:

MyClass::~MyClass()
{
   EnterCriticalSection(&cs);

//Access Data Members, **NO Global** members are being accessed here


  LeaveCriticalSection(&cs);
}

Je me demande si cela aidera par quelque moyen que ce soit de garder le destructeur?

Prenons un scénario:

1. Thread1 - About to execute any of the member function which uses critical section
2. Thread2-  About to execute destructor.

Si l'ordre d'exécution est 1 = <2, cela pourrait fonctionner. Mais que se passe-t-il si l'ordre est inversé?

Est-ce un problème de conception?

Était-ce utile?

La solution

Le destructeur ne doit pas être appelé lorsque l'objet est en cours d'utilisation . Si vous faites face à une telle situation, il faut une solution fondamentale . Toutefois, le destructeur peut vouloir modifier quelque chose (qui n’est pas lié à la classe en cours de destruction) et nécessiter une section critique (par exemple, décrémenter un compteur global ).

Autres conseils

Je pense que vous avez un problème plus fondamental. Il ne devrait pas être légal de détruire votre objet sur un thread alors qu'un autre appelle toujours des fonctions membres. Ceci est faux en soi.

Même si vous protégez votre destructeur avec des sections critiques, que se passe-t-il lorsque l'autre thread commence à exécuter le reste de la fonction? Il le fera sur un objet supprimé qui (en fonction de son emplacement d'allocation) sera une mémoire vide ou un simple objet non valide.

Vous devez modifier votre code pour vous assurer que l'objet n'est pas détruit pendant son utilisation.

Si vous accédez à des variables globales, vous aurez peut-être besoin de la sécurité des threads, oui

par exemple. Mon " Fenêtre " class s’ajoute lui-même à la liste " knownWindows " dans le constructeur et se supprime dans le destructeur. " connusWindows " doit être threadsafe afin qu'ils puissent verrouiller un mutex pendant qu'ils le font.

Par contre, si votre destructeur n'accède qu'aux membres de l'objet en cours de destruction, vous avez un problème de conception.

Définissez "thread safe". Ce sont peut-être les deux mots les moins bien compris de l'informatique moderne.

Mais s'il est possible que le destructeur soit entré deux fois à partir de deux threads différents (comme le suggère l'utilisation d'objets de symchronisation), votre code est en deep doo-doo. Les objets qui suppriment l'objet sur lequel vous demandez des informations doivent gérer cela - c'est (probablement) à ce niveau que la synchronisation devrait avoir lieu.

J'ai vu un cas de threads ACE, le thread s'exécutant sur un objet ACE_Task_Base et l'objet est détruit d'un autre thread. Le destructeur acquiert un verrou et notifie le thread contenu, juste avant d'attendre une condition. Le thread qui s'exécute sur le signal ACE_Task_Base signale la condition à la sortie et laisse le destructeur se terminer et le premier thread sortir:

class PeriodicThread : public ACE_Task_Base
{
public:
   PeriodicThread() : exit_( false ), mutex_()
   {
   }
   ~PeriodicThread()
   {
      mutex_.acquire();
      exit_ = true;
      mutex_.release();
      wait(); // wait for running thread to exit
   }
   int svc()
   {
      mutex_.acquire();
      while ( !exit_ ) { 
         mutex_.release();
         // perform periodic operation
         mutex_.acquire();
      }
      mutex_.release();
   }
private:
   bool exit_;
   ACE_Thread_Mutex mutex_;
};

Dans ce code, le destructeur doit utiliser des techniques de sécurité des threads pour garantir que l'objet ne sera pas détruit avant que le thread qui exécute svc () se ferme.

Ne va pas faire une différence. Si, comme vous le dites, l'ordre des appels est inversé, vous appelez une fonction membre sur un objet détruit et cela va échouer. La synchronisation ne peut pas corriger cette erreur logique (pour les débutants, l'appel de fonction membre tenterait d'acquérir un objet verrou détruit).

J'appuie le commentaire de Neil ButterWorth. Absolument, les entités responsables de la suppression et de l’accès à la myclass devraient contrôler cela.

Cette synchronisation commencera réellement à partir du moment où l'objet de type MyClass sera créé.

Vos commentaires indiquent que les membres du groupe " AUCUN ne sont pas consultés ici". donc je suppose que non. Seul le fil qui a créé un objet doit le détruire, alors de quel autre fil voulez-vous le protéger?

J'aime moi-même la création et la destruction ordonnées, où un seul objet possède toujours un autre sous-objet, et tout autre objet faisant référence à ce sous-objet est un descendant plus bas dans l'arbre. Si l'un de ces sous-objets représente différents threads, ils se seront assurés de terminer avant la destruction dans l'arborescence.

Exemple:

  • main () crée l'objet A
    • objet A contient l'objet B
      • objet B contient l'objet C
        • objet C crée un thread qui accède aux objets A et B
        • Le destructeur de l'objet C s'exécute en attendant la fin de son thread
      • le destructeur de l'objet B s'exécute
    • le destructeur de l'objet A s'exécute
  • main () renvoie

Les destructeurs d'objet A et B n'ont pas besoin de penser aux threads et le destructeur d'objet C n'a besoin que d'implémenter un mécanisme de communication (attente d'un événement, par exemple) avec le thread qu'il a choisi de créer.

Vous ne pouvez avoir des problèmes que si vous commencez à distribuer des références (pointeurs) à vos objets vers des threads arbitraires sans garder trace du moment où ces threads sont créés et détruits, mais si vous le faites, vous devriez utiliser le comptage de références. et si vous l'êtes, il est trop tard au moment de l'appel du destructeur. S'il reste une référence à un objet, personne n'aurait même dû essayer d'appeler son destructeur.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top