2012-02-20 9 views
5

Estoy tratando de encontrar una mejor comprensión de las señales Qt y las ranuras en conjunto con los hilos. Así que he intentado esta aplicación mínima:El hilo Qt no se detiene después de llamar a exit/quit

foo.h:

#include <QObject> 

class A : public QObject { 
    Q_OBJECT 

public: 
    void doit(); 

signals: 
    void x(); 
}; 

class B : public QObject { 
    Q_OBJECT 

public slots: 
    void h(); 
}; 

foo.cpp:

#include "foo.h" 

#include <QThread> 
#include <QCoreApplication> 

void B::h() { 
    qDebug("[%d] B::h() here!", (int) QThread::currentThreadId()); 
    QCoreApplication::instance()->quit(); 
} 

void A::doit() { 
    qDebug("[%d] emitting...", (int) QThread::currentThreadId()); 
    emit x(); 
} 

int main(int argc, char* argv[]) { 
    QCoreApplication app(argc, argv); 
    A a; 
    B b; 
    QObject::connect(&a, SIGNAL(x()), &b, SLOT(h())); 
    QThread t; 
    t.start(); 
    b.moveToThread(&t); 
    a.doit(); 
    t.wait(); 
    return 0; 
} 

todo está bien, sólo el t.wait() al final nunca se regresa. Mi entendimiento es que llamar a quit() debería detener el ciclo de eventos, lo que significa que debe regresar exec() y, por lo tanto, debe ejecutarse() y la ejecución de subprocesos debe detenerse. ¿Me estoy perdiendo de algo?

+3

El nombre de sus métodos debe explicitar su propósito. – UmNyobe

+0

¡No se preocupe, esto no es código de producción! Encuentro que los nombres falsos funcionan mucho mejor en el código de muestra/prueba corto, que los nombres artificialmente descriptivos. – Elektito

+0

Estoy de acuerdo con UmNyobe. Sería más fácil leer y comprender el código de muestra si usó nombres más informativos. P.ej. A :: doit() -> A :: emitThreadStart(), void x() -> startThread(), void h() -> void quitApplication() ... etc – Dmitriy

Respuesta

10

QCoreApplication::quit() no se indica como método seguro para subprocesos, por lo que no puede llamarlo desde otro subproceso. Su aplicación puede fallar o tener un comportamiento indefinido (UB).

t.wait() nunca volverá porque el hilo en ejecución está esperando constantemente eventos. Para detener el hilo debe llamar QThread::quit() [slot]

Si desea salir de la aplicación después de que el trabajo se hace lo que tiene que emitir una señal, que está conectado a QCoreApplication::quit() [static slot]

Si desea detener el subproceso de trabajo después de la trabajo que se han hecho también para emitir una señal, conectada a void QThread::quit() [slot]

Agregado: Threads, Events and QObjects para su posterior lectura

aviso importante: debe llamar QCoreApplication::exec() i n fin de poder utilizar la señal & mecanismo de ranura entre subprocesos, conexiones en cola.

De Qt QThread doc:

Cada QThread puede tener su propio ciclo de eventos. Puede iniciar el ciclo de evento llamando a exec(); puede detenerlo llamando a exit() o quit().Tener un bucle de evento en un hilo hace posible conectar señales de otros hilos a las ranuras en este hilo, utilizando un mecanismo llamado en cola conexiones. También hace posible utilizar clases que requieren el bucle de evento , como QTimer y QTcpSocket, en el hilo. Tenga en cuenta, sin embargo, que no es posible usar ninguna clase de widget en el subproceso .

Doc para Qt :: QueuedConnection:

La ranura se invoca cuando el control vuelve al bucle caso de hilo del receptor. La ranura se ejecuta en el hilo del receptor .

+0

Tiene razón. No estoy deteniendo el ciclo de eventos correcto. Desafortunadamente, UmNyobe señaló esto antes que tú, así que tengo que aceptar su respuesta. Sin embargo, estoy votando tu respuesta, ya que está más claro. Gracias. – Elektito

+0

Pensándolo bien, creo que será mejor que acepte tu respuesta, ya que la otra tiene mucha desinformación y puede ser bastante confusa para otras personas que la leen. – Elektito

+0

Acerca de su aviso agregado, en mi fragmento de código, no he llamado 'QCoreApplication :: exec()' pero las señales/ranuras funcionan. Me parece que el bucle de eventos dentro de QThread es suficiente. – Elektito

2

Parece que hay muchas cosas malas con su código.

  • No llamas app.exec(). Lo que significa que no hay ningún bucle de evento principal. La señal x de A no se emitirá.
  • Por defecto, un hilo has it's own even loop en Qt (al menos desde hace algunos años). Luego, al iniciar un hilo, llame al Qthread::run(), y se inicia el ciclo de eventos. Ahí es donde está tu hilo, no en t.wait().
  • ¿Cuál es el propósito de t.wait()? Creo que lo estás haciendo mal uso.
  • (Si todo lo demás estaba bien), en B::h() está deteniendo el hilo principal del otro hilo. ¿Es eso lo que querías hacer?

Así que mi primer consejo será agregar app.exec(), ver cómo se comporta. Explica qué estás tratando de hacer y vuelve a escribir algo más. Porque you're doing it wrong

+0

t.start() llama al método run() cuya implementación predeterminada llama a exec(). Como dije, las señales _se_ emiten y reciben correctamente. wait() es el equivalente de pthread_join, o eso dice la documentación, así que todo lo que hago es esperar a que termine el hilo. Finalmente, tu último punto es correcto. No voy a detener el ciclo de evento de hilo. Cambiar esa línea a 'QThread :: currentThread() -> quit()' hace el truco. Gracias. – Elektito

+0

exec() sigue ejecutándose, por lo que wait() no tiene efectos. – UmNyobe

+0

Eso es cierto, por supuesto, pero la intención de la espera() es esperar al final de exec(), por lo que usar wait() no es incorrecto per se; no detener el ciclo de evento correcto es. – Elektito

Cuestiones relacionadas