user interface - Qt señalización a través de hilos, uno es hilo GUI?
user-interface signals-slots (5)
¿Qué significa mover un objeto de un hilo a otro en Qt usando moveToThread? Todo parece funcionar incluso antes de usar moveToThread, que mueve el objeto de un hilo (hilo GUI) a otro hilo (trabajado) y Qt: connect llama al slot apropiado en el objeto.
¿Hay alguna diferencia debido a dónde vive el objeto, el hilo de GUI o el hilo de trabajo?
EDITAR: Hice un pequeño programa, pero no entiendo cómo funciona QThread junto con la función de señal y ranura, agradecería que me explicaras cuál es el uso de moveToThread con el ejemplo
#include <QtGui/QApplication>
#include <QPushButton>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QString>
#include "mythread.h"
//GUI calls a thread to do some job and sub update the text box once it is done
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget w;
QHBoxLayout * pH = new QHBoxLayout(&w);
QPushButton * pushButton = new QPushButton("asdad");
QLineEdit * lineEdit = new QLineEdit("AAA");
pH->addWidget(pushButton);
pH->addWidget(lineEdit);
w.setLayout(pH);
w.show();
MyThread thread;
qDebug("Thread id %d",(int)QThread::currentThreadId());
QObject::connect(pushButton,SIGNAL(clicked()),&thread,SLOT(callRun())) ;
QObject::connect(&thread,SIGNAL(signalGUI(QString)),lineEdit,SLOT(setText(QString)));
return a.exec();
}
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QMutex>
class MyThread : public QThread
{
Q_OBJECT
public:
MyThread();
public slots:
void callRun();
void run();
signals:
void signalGUI(QString);
private:
QMutex mutex;
};
#endif // MYTHREAD_H
#include "mythread.h"
#include <QDebug>
#include <QString>
#include <QMutexLocker>
MyThread::MyThread()
{
}
void MyThread::callRun()
{
qDebug("in thread");
if(!isRunning())
{
this->start(LowestPriority);
exec();
}
else
{
run();
}
}
void MyThread::run()
{
QMutexLocker fn_scope(&mutex);
static int a = 0;
++a;
qDebug("Thread id inside run %d",(int)QThread::currentThreadId());
this->sleep(3);
static QString number;
QString temp;
number += temp.setNum(a);
emit signalGUI(number);
}
El
QThread::start()
crea el hilo y llama a su implementaciónrun()
. Si desea manejar eventos o señales recibidas en el hilo, debe llamar aQThread::exec()
dentro de la implementación derun()
. Nunca debe llamar arun()
explícitamente y nunca debe llamar aexec()
fuera derun()
.El hilo del propietario hace una diferencia solo cuando un slot está conectado a una señal con un tipo de conexión distinto de
Qt::DirectConnection
. Entonces Qt se asegurará de que la ranura se ejecute en el hilo del propietario, pero para eso el hilo propietario debe estar ejecutando un bucle de evento conQThread::exec()
. En este caso, llamar amyObj.moveToThread(myThread)
garantizará quemyObj
ranurasmyObj
ejecuten en el hilomyThread
.El objeto de subproceso pertenece al subproceso donde se creó, no en el subproceso que administra (y donde se ejecutará el método de ejecución). Entonces, cuando conecta una señal a la ranura de un objeto de subproceso, esa ranura se ejecutará en el subproceso donde se creó el objeto de subproceso a menos que llame a
moveToThread()
.
Al mover un objeto entre hilos, usted decide a qué evento pertenece. Al hacer conexiones dentro de un hilo, el código de señalización llama directamente a cada una de las ranuras (teniendo que esperar a que finalicen). La señalización a través de los límites del hilo coloca la señal de llamada en el bucle de evento, permitiendo que el hilo de la ranura haga la llamada a la ranura cuando esté listo.
Hacer llamadas directas entre hilos requiere que se asegure de que sus funciones vuelven a entrar. También debe asegurarse de proteger sus datos utilizando mutexes o semáforos y al mismo tiempo evitar condiciones de carrera.
En el artículo, supongo que la demora se debe a que la llamada fue directa, es decir, no se procesó en absoluto en segundo plano (pero solo he desgranado el texto).
Eche un vistazo a las señales y las ranuras en los hilos . Si siempre usa señales y ranuras para comunicarse con el hilo de trabajo, Qt maneja el moverToThread por usted si es necesario y usted usó la conexión correcta.
Editar: Supongo que el autor del artículo estaba viendo su problema, ya que estaba llamando al inicio en el constructor antes de que se creara realmente el hilo. En otras palabras, no confíes ciegamente en el código de un tercero.
Editar: en respuesta a su comentario, mire el ejemplo de Mandelbrot , bajo el encabezado MandelbrotWidget Class Implementation
:
Con las conexiones en cola, Qt debe almacenar una copia de los argumentos que se pasaron a la señal para que pueda pasarlos a la ranura más adelante. Qt sabe cómo tomar una copia de muchos tipos de C ++ y Qt, pero QImage no es uno de ellos. Por lo tanto, debemos llamar a la función de plantilla qRegisterMetaType () antes de poder usar QImage como parámetro en las conexiones en cola.
Creo que esto está un poco desactualizado, aquí están los metadatos válidos . Como las señales y los espacios en los subprocesos usan conexiones en cola, en la mayoría de los casos no debería tener que realizar las llamadas moveToThread.
Editar: Trataré de explicar las cosas con un ejemplo similar:
mythread.h:
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QMutex>
class MyThread : public QThread
{
Q_OBJECT
protected:
virtual void run();
signals:
void signalGUI(QString);
};
#endif // MYTHREAD_H
mythread.cpp:
#include "mythread.h"
#include <QString>
void MyThread::run()
{
qDebug("Thread id inside run %d",(int)QThread::currentThreadId());
static int run = 0;
QString temp = QString("Run: %1").arg(run++);
qDebug("String address inside run %p", &temp);
emit signalGUI(temp);
}
mylineedit.h
#ifndef MYLINEEDIT_H
#define MYLINEEDIT_H
#include <QLineEdit>
class MyLineEdit : public QLineEdit
{
Q_OBJECT
public:
explicit MyLineEdit(QWidget *parent = 0);
public slots:
void setText(const QString &string);
};
#endif // MYLINEEDIT_H
mylineedit.cpp
#include "mylineedit.h"
#include <QThread>
MyLineEdit::MyLineEdit(QWidget *parent) :
QLineEdit(parent)
{
}
void MyLineEdit::setText(const QString &string)
{
qDebug("Thread id inside setText %d",(int)QThread::currentThreadId());
qDebug("String address inside setText %p/n", &string);
QLineEdit::setText(string);
}
main.cpp:
#include <QApplication>
#include <QPushButton>
#include <QHBoxLayout>
#include "mythread.h"
#include "mylineedit.h"
//GUI calls a thread to do some job and sub update the text box once it is done
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget w;
QHBoxLayout * pH = new QHBoxLayout(&w);
QPushButton * pushButton = new QPushButton("Run Thread", &w);
MyLineEdit * lineEdit = new MyLineEdit(&w);
pH->addWidget(pushButton);
pH->addWidget(lineEdit);
w.show();
MyThread thread;
qDebug("Thread id %d",(int)QThread::currentThreadId());
QObject::connect(pushButton,SIGNAL(clicked()),&thread,SLOT(start())) ;
QObject::connect(&thread,SIGNAL(signalGUI(const QString&)),lineEdit,SLOT(setText(const QString&)));
return a.exec();
}
Salida de muestra después de hacer clic en el botón:
Thread id 1088110320 Thread id inside run 1093176208 String address inside run 0x41288350 Thread id inside setText 1088110320 String address inside setText 0x974af58
Como puede ver, el hilo de ejecución es diferente al hilo de la GUI principal. Además, aunque pase una referencia constante a un QString, ya que cruza los límites del hilo lo copia. Le recomiendo que lea Threads y QObject .
algunos objetos solo se pueden usar en el hilo del propietario. por ejemplo, si crea un objeto y lo conecta en un hilo y desea enviar y recibir datos en otro hilo, no es posible. por lo tanto, una solución es mover su objeto de un hilo a otro y operarlo.
#include <QtGui/QApplication>
#include <QPushButton>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QString>
#include "mythread.h"
//GUI calls a thread to do some job and sub update the text box once it is done
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget w;
QHBoxLayout * pH = new QHBoxLayout(&w);
QPushButton * pushButton = new QPushButton("asdad");
QLineEdit * lineEdit = new QLineEdit("AAA");
pH->addWidget(pushButton);
pH->addWidget(lineEdit);
w.setLayout(pH);
w.show();
MyThread thread;
thread.moveToThread(&thread);
thread.start();
qDebug("Thread id %d",(int)QThread::currentThreadId());
QObject::connect(pushButton,SIGNAL(clicked()),&thread,SLOT(callRun()),Qt::QueuedConnection) ;
QObject::connect(&thread,SIGNAL(signalGUI(QString)),lineEdit,SLOT(setText(QString)),Qt::DirectConnection);
return a.exec();
}
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QMutex>
class MyThread : public QThread
{
Q_OBJECT
public:
MyThread();
public slots:
void callRun();
void run();
signals:
void signalGUI(QString);
private:
QMutex mutex;
};
#endif // MYTHREAD_H
#include "mythread.h"
#include <QDebug>
#include <QString>
#include <QMutexLocker>
MyThread::MyThread()
{
}
void MyThread::callRun()
{
QMutexLocker fn_scope(&mutex);
static int a = 0;
++a;
qDebug("Thread id inside run %d",(int)QThread::currentThreadId());
this->sleep(3);
static QString number;
QString temp;
number += temp.setNum(a);
emit signalGUI(number);
}
void MyThread::run()
{
exec();
}
Se crea un nuevo objeto de hilo y el objeto de hilo se mueve al mismo hilo. Las señales se encuentran ahora en los subprocesos y el tipo de conexión está en cola y funciona como se esperaba.