c++ - signal - ¿Cómo emitir señal de hilo cruzado en Qt?
qt signals and slots tutorial (2)
La documentación de Qt indica que las señales y las ranuras pueden ser direct
, en queued
y auto
.
También establecía que si el objeto que posee la ranura ''vive'' en un hilo diferente del objeto que posee la señal, emitir dicha señal será como publicar un mensaje: la señal emitida regresará instantáneamente y el método de ranura se invocará en el ciclo de evento del hilo objetivo.
Lamentablemente, la documentación no especifica que ''lives'' representa y no hay ejemplos disponibles. He intentado con el siguiente código:
main.h:
class CThread1 : public QThread
{
Q_OBJECT
public:
void run( void )
{
msleep( 200 );
std::cout << "thread 1 started" << std::endl;
MySignal();
exec();
}
signals:
void MySignal( void );
};
class CThread2 : public QThread
{
Q_OBJECT
public:
void run( void )
{
std::cout << "thread 2 started" << std::endl;
exec();
}
public slots:
void MySlot( void )
{
std::cout << "slot called" << std::endl;
}
};
main.cpp:
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
CThread1 oThread1;
CThread2 oThread2;
QObject::connect( & oThread1, SIGNAL( MySignal() ),
& oThread2, SLOT( MySlot() ) );
oThread1.start();
oThread2.start();
oThread1.wait();
oThread2.wait();
return a.exec();
}
La salida es:
thread 2 started
thread 1 started
MySlot()
nunca se llama :(. ¿Qué estoy haciendo mal?
No subclases QThread para Qt 4.4+
Si bien la respuesta de Aiua es buena, quiero señalar algunos problemas con QThread y Qt 4.6 o 4.7.
Este artículo lo resume: http://blog.qt.io/blog/2010/06/17/youre-doing-it-wrong/
La falta de documentación en la parte de Qt
Lamentablemente, el problema se debe a la falta de actualizaciones de la documentación. Antes de Qt 4.4, QThread no tenía implementación run () predeterminada, lo que significaba que tenía que subclasificar QThread para usarlo.
Si usa Qt 4.6 o 4.7, casi con seguridad no debería subclasificar QThread.
Use moveToThread
La clave para lograr que las ranuras se ejecuten en un hilo de trabajo es usar el método moveToThread como lo señaló Aiua.
Hay bastantes problemas con su código:
- como dice Evan, falta la palabra clave emitir
- todos tus objetos viven en el hilo principal, solo el código en los métodos de ejecución vive en otros hilos, lo que significa que el slot de MySlot sería llamado en el hilo principal y no estoy seguro de que sea eso lo que quieres
- nunca se llamará a su ranura ya que el bucle de evento principal nunca se lanzará: sus dos llamadas a wait () solo se agotarán después de mucho tiempo (y probablemente matará su aplicación antes de que eso ocurra) y no creo eso es lo que quieres, de todos modos, realmente no tienen uso en tu código.
Es muy probable que este código funcione (aunque no lo he probado) y creo que hace lo que usted quiere que haga:
class MyObject : public QObject
{
Q_OBJECT
public slots:
void MySlot( void )
{
std::cout << "slot called" << std::endl;
}
};
class CThread1 : public QThread
{
Q_OBJECT
public:
void run( void )
{
std::cout << "thread 1 started" << std::endl;
int i = 0;
while(1)
{
msleep( 200 );
i++;
if(i==1000)
emit MySignal();
}
}
signals:
void MySignal( void );
};
class CThread2 : public QThread
{
Q_OBJECT
public:
void run( void )
{
std::cout << "thread 2 started" << std::endl;
exec();
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
CThread1 oThread1;
CThread2 oThread2;
MyObject myObject;
QObject::connect( & oThread1, SIGNAL( MySignal() ),
& myObject, SLOT( MySlot() ) );
oThread2.start();
myObject.moveToThread(&oThread2)
oThread1.start();
return a.exec();
}
Ahora MyObject vivirá en thread2 (gracias a moveToThread).
MySignal debe enviarse desde thread1 (pensé que no estoy seguro de eso, podría enviarse desde el hilo principal, realmente no importa).
No se necesita ningún bucle de evento en thread1 ya que la emisión de una señal no necesita un bucle de evento. Se necesita un bucle de evento en thread2 (lanzada por exec ()) para recibir la señal.
MySlot se llamará en thread2.