IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Threads & Processus C++ Discussion :

Destruction d'objet inattendue lors d'un multithread


Sujet :

Threads & Processus C++

  1. #1
    Membre actif

    Homme Profil pro
    Étudiant
    Inscrit en
    Juillet 2014
    Messages
    103
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2014
    Messages : 103
    Points : 224
    Points
    224
    Par défaut Destruction d'objet inattendue lors d'un multithread
    Bonjour,

    Je pense que le mieux est d'exposer mon problème schématiquement.

    J'ai un objet A et un objet B. A possède un pointeur vers B et instancie B dynamiquement via une fonction A::fonctionA().

    Ensuite, A::fonctionA() :
    - crée un thread concurrent A::fonctionA2()
    - parallèlement à ce thread, exécute en même temps la fonction Sleep().

    Après la fin de la fonction Sleep(), les données de B sont effacées mystérieusement alors que le destructeur de B n'est jamais appelé.

    À votre avis, d'où cela pourrait-il venir ?

    Je précise que je débute en multithreading, donc toute suggestion ou mise en garde serait la bienvenue !

    Merci.

  2. #2
    Membre confirmé Avatar de KsassPeuk
    Homme Profil pro
    Ingénieur Chercheur
    Inscrit en
    Juillet 2013
    Messages
    138
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur Chercheur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2013
    Messages : 138
    Points : 635
    Points
    635
    Par défaut
    Lu'!

    Citation Envoyé par Jimmy91 Voir le message
    Je précise que je débute en multithreading, donc toute suggestion ou mise en garde serait la bienvenue !
    Quand tu fais du multi-threading, le but c'est que toutes les ressources que tu manipules ne soient jamais partagées, sauf, un conteneur thread-safe que tu n'écris pas toi même sauf si tu sais ce que tu fais. En résumé, pour faire du bon code concurrent, le mieux c'est d'écrire du code qui n'agit pas de manière concurrente.

    Le mieux, ce serait que tu nous montres un bout de code minimal qui reproduit ce comportement (ça va être la grosse poilade de faire un bug reproductible sur un code concurrent). Et également que tu nous dises quel est concrètement le problème que tu essaies de résoudre, peut être que c'est simplement la conception que tu as choisi qui ne tient pas la route.

  3. #3
    Membre actif

    Homme Profil pro
    Étudiant
    Inscrit en
    Juillet 2014
    Messages
    103
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2014
    Messages : 103
    Points : 224
    Points
    224
    Par défaut
    Citation Envoyé par KsassPeuk Voir le message
    Le mieux, ce serait que tu nous montres un bout de code minimal qui reproduit ce comportement (ça va être la grosse poilade de faire un bug reproductible sur un code concurrent). Et également que tu nous dises quel est concrètement le problème que tu essaies de résoudre, peut être que c'est simplement la conception que tu as choisi qui ne tient pas la route.
    Bonjour KsassPeuk et merci pour votre réponse rapide !

    Mon programme est simple : il lit des données puis les affiche dans une MessageBox. L'affichage doit avoir lieu en même temps qu'une attente système et ne doit pas bloquer le processus d'attente, et c'est pour cela que l'affichage se fait dans un thread séparé.

    Il est vrai que je pourrais programmer l'affichage autrement et que cette manière de faire est très critiquable, mais j'ai aussi envie d'apprendre à utiliser les threads.

    Pour vous faciliter la tâche, je vous fournis un extrait simplifié de mon code :

    Le programme est supervisé par un Controller (Controller.h) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    #include <pthread>
    #include <windows>
     
    class Controller
    {
    	/* Input lit et stocke les données du fichier spécifié. 
    	Dans mon code, ce sont les données de input qui sont supprimées mystérieusement. */
    	Input* input;
     
    	/* Les deux chaines de caractères suivantes servent à stocker le contenu des 
    	messages à faire passer pendant la création de threads (cf. plus loin) */
    	std::string message;
    	std::string title;
     
    	public :
     
    	Controller();
    	~Controller();
    	/* La fonction principale, exécutée durant le thread principal */
    	void run();
     
    	/* Ces fonctions affichent une Message Box Windows dans un thread
    	(en effet, une Message Box Windows bloque le processus courant 
    	tant qu'on n'a pas cliqué sur le bouton OK, ce que je veux éviter) */
    	void printMessage(std::string message_, std::string title_);
    	static void* displayMessage(void* data);
    };
    Controller.cpp :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
     
    #include "Controller.h"
     
    Controller::Controller()
    {
     
    }
     
    Controller::~Controller()
    {
     
    }
     
    void Controller::run()
    {
    	input = new Input();
    	input->importData("C:\myFile.txt");	
     
    	std::string messageTemp;
     
    	messageTemp = input->getData1();
     
    	printMessage(messageTemp, "Information");  // On affiche une des données fournies par input.
    	Sleep(50000);		/* L'attente système doit commencer immédiatement
    	et pas seulement après que le bouton OK de la Message Box appelée par
    	printMessage() soit cliqué */
     
    	messageTemp = input->getData2(); // Ici les données de input ont été effacées ! Pourtant, il n'y a pas d'accès concurrent au controller courant puisqu'il est copié, non ?
     
    	printMessage(messageTemp, "Information"); // Erreur : violation d'accès
    }
     
    void Controller::printMessage(std::string message_, std::string title_)
    {
    	pthread_t th;
    	message = message_;
    	title = title_;
    	Controller* copy = new Controller(*this);
     
    	pthread_create(&th, NULL, displayMessage, copy);		/* Le seul moyen que j'ai trouvé de transmettre les deux
    	chaines de caractères à la fonction d'affichage des messages 
    	a été de copier le controller courant et de le transmettre via la fonction pthread_create */	
    }
     
    void* Controller::displayMessage(void* data)
    {
    	Controller* cData = (Controller*) data;
    	MessageBox(cData->message, cData->title); /* J'ai un peu oublié le prototype de 
    	la Message Box mais c'est pour comprendre */
    	delete cData;
    	delete data;
    	pthread_exit(NULL); // On sort du thread créé dans printMessage()
    }
    main.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    void main()
    {
    	Controller* ctrl = new Controller();
    	ctrl->run();
    }
    Merci pour vos réponses !

  4. #4
    Membre confirmé Avatar de KsassPeuk
    Homme Profil pro
    Ingénieur Chercheur
    Inscrit en
    Juillet 2013
    Messages
    138
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur Chercheur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2013
    Messages : 138
    Points : 635
    Points
    635
    Par défaut
    Tu as une raison particulière pour ne pas utiliser std::thread ? Ce serait standard, et accessoirement : typé et plus facile d'utilisation.

    Après, il y a plein de problèmes dans ton code. La majorité (pour ne pas dire la totalité) d'entre eux sont dus au fait que tu utilises des pointeurs nus et de l'allocation/désallocation dynamique manuelles à tord et à travers de manière injustifiée. Par exemple, tu supprimes deux fois la ressource associée au même pointeur.

    Un code bien plus simple avec std::thread pourrait ressembler à ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
     
    #include <thread>
    #include <string>
     
    class Controller{
    public:
      void run(){
        Input input;
     
        std::string message = input.getData1();
     
        std::thread t{ std::bind(&Controller::printMessage, this, message, "Information") };
     
        std::string other = input.getData2();
     
        t.join();
      }
     
      void printMessage(std::string const& message, std::string const& title){
        //faire des trucs d'affichage.
      }
     
    };
     
    int main(){
      Controller c;
      c.run();  
     
      return 0;
    }

  5. #5
    Membre éprouvé Avatar de fenkys
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Octobre 2007
    Messages
    376
    Détails du profil
    Informations personnelles :
    Âge : 56
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 376
    Points : 1 054
    Points
    1 054
    Par défaut
    Bonjour Jimmy91,

    Pour faire simple :
    - tu n'as pas de constructeur de copie. Or quand tu as un pointeur nu dans une classe tu dois avoir un constructeur de copie.
    - dans void* Controller::displayMessage(void* data), tu effaces deux fois le même objet. cData est une copie de data et tu les delete tous les deux. Je suis d'ailleurs surpris que tu n’aies pas au moins un warning à la compilation (mon compilo râle quand je delete un void*).

    Quand tu ne définis pas de constructeur de copie, le compilateur en crée un qui effectue une copie membre à membre. C'est donc ton pointeur qui est copié, pas l'objet pointé, qui est donc partagé entre les deux instances de Controller. Quand tu le delete, tu détruits l'objet pointé pour ces deux instances.

  6. #6
    Membre actif

    Homme Profil pro
    Étudiant
    Inscrit en
    Juillet 2014
    Messages
    103
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2014
    Messages : 103
    Points : 224
    Points
    224
    Par défaut
    Citation Envoyé par KsassPeuk Voir le message
    Tu as une raison particulière pour ne pas utiliser std::thread ? Ce serait standard, et accessoirement : typé et plus facile d'utilisation.
    Bonjour,

    Merci pour ce conseil ! J'utilisais les pthreads car je ne connaissais tout simplement pas l'existence de std::thread et parce que j'avais vu un tutoriel sur les pthreads. Il est vrai que la librairie standard est bien plus simple d'utilisation, je suis en train de modifier mon code en conséquence. Merci également de m'avoir fourni un exemple.

    À ceux qui comme moi sont débutants en multithreading et voudraient s'initier à l'utilisation de std::thread, je recommande le tutoriel de Baptiste Wicht suivant le lien : http://baptiste-wicht.com/posts/2012...t-threads.html

    Citation Envoyé par fenkys Voir le message

    cData est une copie de data et tu les delete tous les deux.
    Merci également à fenkys pour vos corrections sur mon code mal structuré. Si je comprends bien, comme Controller ne possède pas de constructeur de copie, cData n'est qu'une simple copie du pointeur data et un delete data conduit à la destruction de l'objet pointé par data et cData, ce qui pourrait très mal se passer si je fais un appel supplémentaire à delete cData. Je prends toutes vos remarques en compte dans mon code.

    Encore merci à tous les deux !

    Cordialement,

    Jimmt91

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [1.2] Problème lors de la destruction d'objet
    Par zithum dans le forum OpenSceneGraph
    Réponses: 3
    Dernier message: 18/05/2010, 11h54
  2. Destruction d'objet (.free) >> EAccessViolation
    Par monstroplante dans le forum Langage
    Réponses: 7
    Dernier message: 08/11/2005, 20h19
  3. destruction d'objets dans un vecteur
    Par titouille dans le forum C++
    Réponses: 12
    Dernier message: 28/07/2005, 19h20
  4. [débutante][Concept] Destruction d'objet, mode d'emploi?
    Par skea dans le forum Général Java
    Réponses: 4
    Dernier message: 12/06/2004, 21h48
  5. Réponses: 10
    Dernier message: 01/12/2003, 23h17

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo