2011-02-04 27 views
12

Terminé de implementar un programa Go donde el ser humano puede interrumpir en cualquier momento el software para ordenar su reproducción. Básicamente, tengo un algoritmo ejecutándose en otro hilo que tiene en todo momento el "Mejor movimiento" disponible, que sigue mejorando.Cómo interrumpir correctamente un bucle infinito QThread

Mi pregunta es: ¿cómo puedo interrumpir correctamente un hilo en un ciclo infinito?

He intentado algunas cosas, y decidí hacer esto:

class MyWorker : public QObject 
{ 
    Q_OBJECT 
public: 
    MyWorker(); 
    ~MyWorker(); 

public: 
    ThreadControl * getThreadControl(); 

public slots: 
    void work(); 

private: 
    void endOfComputation(); 

private: 
    ThreadControl * threadControl; 
} 

Aviso que don't subclass QThread:

class ThreadControl : public QObject 
{ 
    Q_OBJECT 
public: 
    ThreadControl(); 

public: 
    bool getAbort(); 
    Parameter getParameter(); 
    void setParameter(Parameter & param); 

public slots: 
    void setAbort(bool b); 

private: 
    QMutex mutex; 
    bool abort; 
    Parameter param; 
}; 

Y, por último, el bucle infinito se codifica como esto:

void Myworker::work() 
{ 
    // ... 
    forever 
    { 
     abort = threadControl->getAbort(); 
     if(abort) 
     { 
      break; 
     } 
     // ... 
    } 
    endOfComputation(); 
} 

Entonces, como se puede adivinar, en el main, Me llaman regularmente ThreadControl::setAbort(true)

Básicamente, yo sigo un puntero a un valor lógico en el hilo principal y activarlo cuando quiero. (El booleano está encapsulado en ThreadControl por lo que puedo bloquearlo correctamente con un mutex). Hasta ahora todo ha sido bueno para mí ... ¡Pero me parece repugnante! Alternar punteros a booleanos suena como ... programación incorrecta para mí ...

El problema es que la documentación en la web es en su mayoría (¿totalmente?) Sobre hilos de productores y consumidores, que terminan después de un tiempo finito, no cuando se lo piden a. No se desarrolla la interrupción de un hilo, es por eso que estoy preguntando: ¿Hay una mejor manera?

Respuesta

5

Eso suena una buena manera de hacerlo. También puede ver cómo se ha hecho boost thread. Debido a que usa excepciones para abortar el hilo, le permite interrumpir el hilo en varios lugares usando interruption_point. El uso de su modelo de hilo se podría escribir la función de su hilo de esta manera:

void myFunction(){ 
    boost::this_thread::interruption_point(); 
} 

void Myworker::work() 
{ 
    // ... 
    try 
    { 
     forever 
     { 
      boost::this_thread::interruption_point(); 
      // do some work 
      boost::this_thread::interruption_point(); 
      // work again 
      myFunction(); // interruption might be triggered inside this function 
     } 
     endOfComputation(); 
    } 
    catch(boost::thread_interrupted const &){ 
     // The thread has been interrupted 
    } 
} 

Creo que internamente, que utilizan un valor lógico (por hilo), que se establece en true cuando se llama boost::thread::interrupt() método.

EDITAR

Mi objetivo es mostrar cómo se potencie resuelto este problema. Por supuesto, esto no funcionará con su QThread. No quiero que cambies a boost :: thread tampoco.

Edit2 aplicación rápida con QThread:

void function(); 

class MyWorker : public QThread { 
public: 

    MyWorker() : m_isInterrupted(false) {} 

    class InterruptionException { 
    public: 
     InterruptionException(){} 
    }; 
    static void interruptionPoint(){ 
     MyWorker * myWorker = dynamic_cast<MyWorker*>(QThread::currentThread()); 
     if(myWorker){ 
      if(myWorker->m_isInterrupted){ 
       throw InterruptionException(); 
      } 
     } 
    } 

public slots: 
    void interrupt(){ 
     m_isInterrupted = true; 
    } 
    void work(){ 
     try { 
      while(true){ 
       MyWorker::interruptionPoint(); 
       function(); 
      } 
     } 
     catch(InterruptionException const &){ 

     } 
    } 

private: 
    bool m_isInterrupted; 
}; 

void function(){ 
    MyWorker::interruptionPoint(); 
} 
+1

hilo impulso no funciona con QThread. la gente usa qthread a menudo porque es una aplicación qt gui. –

+0

Mi objetivo era proporcionar otra forma de interrumpir ese hilo. Se agregó una implementación de muestra. – tibur

7

entiendo que alternar banderas lugar de llamar a algún tipo de métodos especializados se ve feo, pero en la vida real que es la mejor manera de hacerlo. Los métodos especializados son generalmente muy peligrosos, vea QThread::terminate() por ejemplo. Algunos entornos proporcionan indicadores listos para usar, por lo que no es necesario que agregue sus propios booleanos, como Java con su Thread.interrupt() y Thread.interrupted(). Qt no tiene tal cosa, y tal vez eso también es bueno porque la interrupción a veces funciona de manera contra-intuitiva en Java. Tome la diferencia entre Thread.interrupted() y Thread.isInterrupted() por ejemplo. Es absolutamente contrario a la intuición.A menos que consulte los documentos, difícilmente podrá adivinar cuál es la diferencia. Peor aún, ya que uno de ellos es estático, puede pensar que es la diferencia, pero no lo es. Además, las operaciones de E/S antiguas no se pueden interrumpir en Java, pero sí en NIO de nuevo estilo, lo que tampoco tiene sentido.

A veces puede evitar las banderas mediante el uso de otra cosa. Por ejemplo, si un hilo tiene algún tipo de cola de entrada de elementos para procesar, puede usar un elemento especial al final de la cola para indicar que debe detenerse allí mismo. Pero a veces no hay un lugar conveniente para poner esto, es cuando se usan indicadores booleanos.

Lo único que podría hacer para optimizar un poco su código es reemplazar el bloqueo mutex con un bool volátil. Sin embargo, esto no garantizará el orden de acceso a la memoria, de modo que si su hilo depende de algo que sucede alrededor de la escritura volátil, no debería ir de esta manera. O podría usar un QAtomicInt en lugar de sus barreras de memoria. Pero si no hay un impacto significativo en el rendimiento, usar un mutex es bueno y más seguro también.

También me sustituyo el lazo con:

while (!threadControl->getAbort()) { 
    // ... 
+0

¿Podría vincular algunos ejemplos de la contrainguitividad de Thread.interrupt() e interrumpido() de Java? –

+1

@Josiah, no se me ocurre ningún _links_, pero veré mi edición. –

+0

Gracias. ¡Esa es una observación útil! –