2015-11-29 1 views
0

J'ai écrit une classe nommée Task qui encapsule un boost :: thread et permet de surcharger la méthode run() pour faire du travail sur le thread nouvellement créé.Boost Échec du démarrage du thread si l'objet thread déclaré comme membre

Voici la classe de base:

class Task { 


    typedef boost::function<void()> TaskEventCallback; 
    typedef boost::unordered_map<string, TaskEventCallback> Callbacks; 
    typedef boost::unordered_map<string, Callbacks> SessionTaskMap; 
    typedef boost::unordered_map<TaskListener *, SessionTaskMap> ListenersMap; 

    public: 
     Task(NGSAppServer& server); 
     Task(const Task& orig); 
     virtual ~Task(); 

     virtual void run() = 0; 
     bool start(); 
     bool pause(); 
     bool cancel(); 

     virtual bool registerListener(TaskListener *); 
     virtual bool unregisterListener(TaskListener *); 
     string getProgress(); 
     string getStatusMessage(); 

     boost::thread * getThread(); 

    protected: 
     void postEvent(string event); 
     void startThread(); 

     void setProgress(string progress); 
     void setStatusMessage(string statusMessage); 


     vector<TaskListener *> listeners; 
     bool taskRunning; 
     bool taskStarted; 
     bool taskCanceled; 
     bool taskEnded; 
     NGSAppServer& server; 

     boost::thread worker; 
     boost::recursive_mutex mutex; 

     ListenersMap listeners_map; 

    private: 
     string progress; 
     string statusMessage; 

}; 

La classe est en mesure d'afficher les événements à plusieurs session http via la classe de serveur, mais ce n'est pas pertinent ici. Tout fonctionne bien, le fil commence et affiche des événements avec succès, jusqu'à la fin du travail. Ceci est un extrait de travail:

 RestoreTask * task = new RestoreTask(application->getServer()); 
     TaskListener * listener = new TmpTL(*task, progressText, this); 
     task->start(); 

Et ceci est la classe de restauration:

 class Restore : public Task { 

     public: 
      Restore(NGSAppServer& server); 
      Restore(const Restore& orig); 
      virtual ~Restore(); 

      virtual void run(); 

     private: 
      ... stuffs ... 
     }; 

Maintenant, je suis tenté de diviser le travail de restauration tâche en N sous-tâches (travailleur, aussi une sous-classe de Tâche). Voici la nouvelle méthode d'exécution de la restauration:

  std::vector<Worker *> workers; 
      for(uint i = 0; i < 2; i++){ 
       //Start tread 
       Worker _worker(this, server); 
       _worker.start(); 
       workers.push_back(&_worker); 
      } 

      //Join Workers 
      for(uint i = 0; i < 2; i++){ 
       workers.at(i)->getThread()->join(); 
      } 

Ce code échoue depuis le début du fil d'enfant à créer un sigfault tout en essayant d'exécuter la méthode d'exécution de la classe des travailleurs car il est rapporté que et virtuelle pure par ailleurs la tentative de verrouiller le mutex sur la classe de base de travail échoue sur cette affirmation:

void boost::recursive_mutex::lock(): Assertion `!pthread_mutex_lock(&m)' failed. 

créer directement un objet travailleur et à partir (comme pour la restauration) ne crée pas de problème! À première vue, il peut sembler que la méthode Restore run() s'est arrêtée avant les threads enfants, en supprimant les instances Worker, puis en exécutant l'appel sur la classe de base (virtual virtuel) et en essayant d'accéder à un mutex détruit. (S'IL VOUS PLAÎT CORRIGER MOI SI JE SUIS MAUVAIS ICI!)

En utilisant le débogueur pour approfondir le problème, j'ai trouvé que ce n'est pas le cas. Le problème semble rester à l'intérieur des objets déclaration des travailleurs depuis le changement suivant rendre le code fonctionne sans aucun problème:

  std::vector<Worker *> workers; 
      for(uint i = 0; i < 2; i++){ 
       //Start tread 
       Worker * _worker = new Worker(this, server); 
       _worker->start(); 
       workers.push_back(_worker); 
      } 

      for(uint i = 0; i < 2; i++){ 
       workers.at(i)->getThread()->join(); 
       delete workers.at(i); 
      } 

Je préfère créer des travailleurs sans le nouvel opérateur depuis que je ne ai pas vraiment besoin de garder ces Les objets en vie après que Restore :: run() soit fini et je devrais être en mesure de garantir que ces objets existeront encore jusqu'à ce que les enfants soient complétés en raison de la jointure des threads (ce qui a été vérifié avec le débogueur).

Qui peut trouver le goulot d'étranglement ici?

J'ai été en mesure de trouver un moyen d'exécuter mon code, la solution (mais pour moi plus important est l'explication ici) est toujours manquant.

Cordialement

Répondre

0

Le _worker se sortir hors de portée et de la boucle quand destructed est répétée. Vous pouvez mettre une impression dans le destructeur pour le vérifier. Ce que vous faites dans la seconde (la nouvelle ..delete) est la bonne chose, peut-être vous pouvez utiliser le smart_ptr/make_ptr pour éviter les suppressions.

Vous pouvez aussi faire un tableau de travailleurs au lieu de boucle for, auquel cas vous devrez utiliser cteur par défaut, et passer initializers (cela, commencer) d'une autre manière

+0

simple et clair! Je cherchais une montagne en cherchant une fourmi! Heureux comme des idiots, je peux dormir maintenant :) Beaucoup de mercis! – Gianks