subproceso como clase, ¿cómo asegurar que los datos de las subclases se destruyan después?

Escribí un contenedor de subprocesos nativos de pthread / windows que imita los subprocesos de Java.

class Thread{
public:
  virtual void run()=0;
  void start();
  void join();

  virtual ~Thread(){
   join(); 
  }
};

Suponer

 class B:public Thread{
  int x;     
  void run(){
    while some condition do soemething on x;
  }
};

Si se destruye una instancia de B antes de que vuelva run(), el subproceso seguirá accediendo a x, y esto es claramente indeseable. Para resolver el problema necesito agregar

B::~B(){
 join();
} 

porque Thread::~Thread() se llama después de que se destruye B::x, por lo que la combinación no tendría ningún efecto.

Sin embargo, si tengo la clase C: pública B con algunos datos adicionales, aún necesito definir C::~C(){join();}

Y así sucesivamente, en toda la jerarquía

Una alternativa seria hacer

template<typename R>
 struct SafeThread:public R,public Thread{
  void run(){
    R::run();
  }
};

para que los datos R (nuestro B::x anterior) se destruyan después de que el hilo se haya unido. Sin embargo, todavía

class B{
  virtual void f(){...}
  void run(){ f();}
};


class C:public SafeThread<B>{
  int y;
  void f(){
  ...something on y;
  }

}; 

seguiría teniendo problemas similares. ¿Cree que hay alguna solución elegante para resolver el problema de garantizar que los datos se destruyan después de que finalice el método de ejecución, sin obligar a cada subclase SC a definir SC::~SC(){join();} ?

preguntado el 03 de mayo de 12 a las 15:05

en cuanto al código de Nick, no, porque de esa manera se llama al deconstructor de la clase base después de que B::x ya no esté -

Solo como referencia, C++ no es Java. Debido a que el GC de Java prácticamente le permite iniciar un hilo y olvidarse de él, permitiéndole mantenerse vivo hasta que termine, la API de subprocesos de Java puede aprovechar eso. no puedes -

Asegurarse de que la vida útil de todos los datos a los que accede un subproceso exceda la vida útil del subproceso funciona bien, ya que nunca termina explícitamente los subprocesos (grupos o subprocesos de vida útil de la aplicación). Si no tengo que llamar a join() por el resto de mi vida, todavía será un millón de años demasiado pronto. -

Debería considerar usar el otros Opción de Java: implementar Runnable en lugar de extender Thread. Ese es un diseño mucho mejor, un hilo is no el código que ejecuta el subproceso, sino una entidad que gestiona la ejecución de ese código. -

+1 @David. Solución mucho más limpia, en mi opinión. Incluso Java no recomienda extender Thread, a menos que usted tienen por alguna extraña razón. Lo cual, no tan extrañamente, nunca he tenido que hacerlo. -

2 Respuestas

puedes escribir tu Thread clase como plantilla aceptando cualquier clase con operator(), que se llama en consecuencia cuando Thread::run() se llama. El siguiente código muestra la idea. Haz cambios si es necesario.

template<typename T>
class MyThread
{
    T & _obj;
    MyThread() = delete;
    MyThread(T& o) : _obj(o) {}
    void run() 
    {
        _obj();
    }
    // other member functions
};

struct B {
    int data_to_process;
    void operator()() {
        // do something with data
    }
}

// usage
B b;
MyThread<B> t(b);
t.run();

contestado el 03 de mayo de 12 a las 15:05

Gracias, tengo una implementación que imita esto, con la interfaz "Runnable" y un Thread::Run(Runnable&) estático como sugiere @David. Quería cambiar a la solución Subclass:public Thread para evitar la necesidad de crear dos objetos cada vez, pero si no existe otra solución, me quedaré con esta implementación anterior, ¡gracias! - Fabio Dalla Libera

@ user2k5: Nunca antes había visto esa sintaxis utilizada para declarar un constructor. ¿Qué significa exactamente? - chao

@FabioDallaLibera: no hay razón para cambiar de un diseño funcional a un diseño peor :) - David Rodríguez - Dribeas

#cHao: es una característica de c++11. Si usted (erróneamente) declara MyThread t; sin ningún objeto pasado, el compilador debería darte un error. - usuario2k5

@DavidRodríguez-dribeas: por el delete, solo quiero asegurarme de que el objeto siempre se pase al hilo, y ciertamente tienes razón sobre el MyThread<B>. - usuario2k5

Creo que solo necesita usar un destructor virtual con la unión de llamada de clase base.

P.ej

class Thread{
public:
  virtual void run()=0;
  void start();
  void join();

protected:
  virtual ~Thread() 
  {
     join( ); 
  }
};

contestado el 03 de mayo de 12 a las 15:05

no, porque de esa manera la clase base desconst se llama en último lugar, es decir, cuando B::x ya se ha ido - Fabio Dalla Libera

Podría valer la pena actualizar su pregunta para explicar esto: Nick

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.