Como emitir sinal de thread cruzado no QT?
-
10-07-2019 - |
Pergunta
A documentação do QT afirma que sinais e slots podem ser direct
, queued
e auto
.
Ele também afirmou que, se o objeto que possui o slot 'vidas' em um fio diferente de objeto que possui sinal, a emissão desse sinal será como postar a mensagem - a emitir o sinal retornará instantaneamente e o método de slot será chamado no loop de eventos do thread do destino.
Infelizmente, a documentação não especifica que 'vidas' representa e nenhum exemplo está disponível. Eu tentei o seguinte 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();
}
A saída é:
thread 2 started
thread 1 started
MySlot()
nunca é chamado :(. O que estou fazendo de errado?
Solução
Existem alguns problemas com seu código:
- como dito por Evan, a palavra -chave emit está faltando
- Todos os seus objetos vivem no tópico principal, apenas o código nos métodos de execução vivem em outros tópicos, o que significa que o slot myslot seria chamado no tópico principal e não tenho certeza se é isso que você quer
- Seu slot nunca será chamado, já que o loop de evento principal nunca será lançado: suas duas chamadas para esperar () só limitarão depois de muito tempo (e você provavelmente matará seu aplicativo antes que isso aconteça) e eu não acho que não Isso é o que você quer, de qualquer maneira, eles realmente não têm utilidade no seu código.
Esse código provavelmente funcionaria (embora eu não o tenha testado) e acho que faz o que você deseja fazer:
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();
}
Agora o MyObject viverá em Thread2 (graças ao MoveTothread).
O MySignal deve ser enviado do Thread1 (pensei que não tenho certeza disso, ele pode ser enviado do thread principal, isso realmente não importa).
Nenhum loop de evento é necessário no Thread1, pois a emitir um sinal não precisa de um loop de eventos. Um loop de evento é necessário no Thread2 (encenado pelo EXEC ()) para receber o sinal.
Myslot será chamado no Thread2.
Outras dicas
Não subclasse qthread para qt 4.4+
Embora a resposta da AIUA seja boa, quero apontar alguns problemas com o QTHREAD e o QT 4.6 ou 4.7.
Este artigo resume: http://blog.qt.io/blog/2010/06/17/youre-doing-it-wrong/
Falta de documentação da parte do QT
Infelizmente, o problema decorre da falta de atualizações para a documentação. Antes do QT 4.4, o QTHREAD não possuía implementação padrão RUN (), o que significava que você precisava subclasse o QTHREAD para usá -lo.
Se você está usando o Qt 4.6 ou 4.7, então quase certamente deveria não Subclasse Qthread.
Use MoveTothread
A chave para obter slots a serem executados em um thread trabalhador é usar o método MoveTothread como apontou a AIUA.
você deve emitir o sinal para iniciar sua função de thread como
emit operateCut(examId,examName_examTemplate[examName].studentIdRec,examName_examTemplate[examName].choiceRecA,examName_examTemplate[examName].choiceRecB,examName_examTemplate[examName].objectRecA,examName_examTemplate[examName].objectRecB);
Você pode adicionar mais de um argumento neste sinal