Qt sinalização entre threads, um é thread da GUI?
-
21-09-2019 - |
Pergunta
O que significa para mover um objeto de um segmento para outro em Qt usando moveToThread?Tudo parece funcionar mesmo antes de utilizar moveToThread, o que move o objeto a partir de uma thread (thread da GUI) para uma outra thread ( trabalhou) e Qt:ligue chama o espaço apropriado no objeto.
Existe alguma diferença, porque de onde o objeto vidas, thread da GUI ou o thread de trabalho?
EDITAR:Eu fiz um pequeno programa, mas eu não entendo como QThread funciona junto com o Sinal e o slot função, eu agradeceria se você pudesse explicar o que é o uso de moveToThread com o exemplo
#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);
}
Solução
Dê uma olhada no Sinais e slots em threads.Se você sempre usar sinais e slots para se comunicar com o thread de trabalho, Qt trata o moveToThread para você, se é necessário e tiver usado a conexão correta.
Editar:Eu acho que o artigo do autor era de ver o seu problema, já que ele estava ligando para iniciar o construtor, antes da thread foi realmente criado.Em outras palavras, não confie código de terceiros cegamente.
Editar:Em resposta ao seu comentário, olhar para o Mandelbrot exemplo, sob a MandelbrotWidget Class Implementation
cabeçalho:
Com fila de espera de ligações, Qt deve armazenar uma cópia dos argumentos que foram passados para o sinal para que possa passá-los para o slot mais tarde.Qt sabe como tirar de cópia de muitos C++ e Qt tipos, mas QImage não é um deles.Devemos, portanto, chamar a função de modelo qRegisterMetaType() antes de podermos usar QImage como parâmetro na fila de conexões.
Eu acredito que isso é um pouco desatualizado, aqui estão os válido meta tipos de.Desde sinais e slots de toda utilizam threads na fila de conexões, você não deve ter para fazer o moveToThread chamadas na maioria dos casos.
Editar:Vou tentar explicar com um exemplo semelhante:
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();
}
Exemplo de saída depois de clicar no botão:
Thread id 1088110320 Thread id inside run 1093176208 String address inside run 0x41288350 Thread id inside setText 1088110320 String address inside setText 0x974af58
Como você pode ver, a linha é diferente do que o principal thread da GUI.Além disso, mesmo que você passe uma constante referência a uma QString, uma vez que ele cruza limites de thread copia-lo.Eu fortemente encorajamos você a ler Linhas e QObject.
Outras dicas
o
QThread::start()
o método cria o tópico e chama seurun()
implementação. Se você deseja lidar com eventos ou sinais recebidos no tópico que você deve ligarQThread::exec()
lado de dentro suarun()
implementação. Você nunca deve ligarrun()
explicitamente e você nunca deve ligarexec()
fora derun()
.O fio do proprietário faz a diferença apenas quando um slot é conectado a um sinal com o tipo de conexão que não
Qt::DirectConnection
. Em seguida, o QT garantirá que o slot funcione no tópico do proprietário, mas para isso o tópico do proprietário deve estar executando um loop de evento comQThread::exec()
. Neste caso, chamandomyObj.moveToThread(myThread)
garantirá issomyObj
slots funcionam no tópicomyThread
.O objeto Thread pertence ao encadeamento onde foi criado, não no encadeamento que ele gerencia (e onde o método de execução será executado). Então, quando você conecta um sinal ao slot de um objeto de rosca, esse slot será executado no fio onde o objeto de thread foi criado, a menos que você ligue
moveToThread()
.
Ao mover um objeto entre os threads, você decide qual loop de evento ele pertence. Ao fazer conexões dentro de um encadeamento, o código de sinalização chama diretamente cada um dos slots (precisando esperar que eles terminem). A sinalização através dos limites do encadeamento coloca a chamada do sinal no loop do evento, deixando o fio do slot fazer a chamada para o slot quando estiver pronta.
Fazer chamadas diretas entre os threads exige que você certifique -se de que suas funções sejam reentrantes. Você também deve proteger seus dados usando mutexes ou semáforos e, ao mesmo tempo, evite as condições de corrida.
No artigo, acho que o atraso se deve ao fato de a chamada ser direta, ou seja, nem um pouco processada em segundo plano (mas eu apenas deslizei o texto).
#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();
}
O novo objeto Thread é criado e o objeto Thread é movido para o mesmo thread. Agora, os sinais estão entre threads e o tipo de conexão são a fila e funciona como esperado.
alguns objetos só podem ser utilizados no segmento proprietário.por exemplo, se você criar e tomada de objeto em uma thread e você deseja utilizar para enviar e receber dados em outro thread é nt possível.portanto, uma solução é mover o seu objeto a partir de um segmento para o outro e de operar sobre ele.