Invocare metodi nel contesto di QThread
-
06-07-2019 - |
Domanda
Nella mia applicazione c'è il thread principale e un thread di lavoro ( QThread
).
Dal thread principale mi piacerebbe invocare un metodo del mio thread di lavoro e farlo funzionare nel contesto del thread.
Ho provato a usare QMetaObject :: invokeMethod
e gli ho dato l'opzione QueuedConnection
ma non funziona.
Ho anche provato a emettere segnali dal thread principale (che è collegato allo slot del thread di lavoro) ma anche questo non è riuscito.
Ecco un frammento di circa quello che ho provato:
class Worker : public QThread
{
Q_OBJECT
public:
Worker() { }
void run()
{
qDebug() << "new thread id " << QThread::currentThreadId();
exec();
}
public slots:
void doWork()
{
qDebug() << "executing thread id - " << QThread::currentThreadId();
}
};
Utilizzando il modo QMetaObject:
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "main thread id - " << QThread::currentThreadId();
Worker worker;
worker.start();
QMetaObject::invokeMethod(&worker, "doWork", Qt::QueuedConnection);
return a.exec();
}
Uso del segnale:
class Dummy : public QObject
{
Q_OBJECT
public:
Dummy() { }
public slots:
void askWork() { emit work(); }
signals:
void work();
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "main thread id - " << QThread::currentThreadId();
Worker worker;
worker.start();
Dummy dummy;
QObject::connect(&dummy, SIGNAL(work()), &worker, SLOT(doWork()), Qt::QueuedConnection);
QTimer::singleShot(1000, &dummy, SLOT(askWork()));
return a.exec();
}
In entrambi i modi, l'ID del thread principale viene stampato nel QThread
doWork
.
Inoltre, ho pensato di implementare un semplice produttore-consumatore, ma se funziona, c'è qualche motivo per non farlo in questo modo?
Soluzione
Il problema era che il ricevitore (il QThread) 'vive' nel thread principale e quindi il loop degli eventi del thread principale è quello che esegue lo slot.
dai documenti di Qt:
Con le connessioni in coda, lo slot viene richiamato quando il controllo ritorna al loop degli eventi del thread a cui appartiene l'oggetto. Lo slot viene eseguito nel thread in cui vive l'oggetto ricevitore.
Quindi la soluzione che ho trovato finora è stata quella di creare un oggetto all'interno del thread run () e utilizzare invece i suoi slot. In questo modo il proprietario del destinatario è il thread e quindi lo slot viene chiamato nel contesto dei thread.
Altri suggerimenti
Questo esempio mostra come puoi dividere la classe Worker per farla funzionare come desideri. È inoltre necessario rendere disponibile un riferimento o un puntatore all'istanza Worker per poter connettersi allo slot.
class Worker : public QObject
{
Q_OBJECT
public:
Worker() { }
public slots:
void doWork()
{
qDebug() << "executing thread id - " << QThread::currentThreadId();
}
};
class WorkerThread : public QThread
{
Q_OBJECT
public:
void run()
{
qDebug() << "new thread id " << QThread::currentThreadId();
Worker worker;
exec();
}
};
Per un semplice esempio produttore-consumatore, dai un'occhiata al blog di Bradley T. Hughes Calpestare senza il mal di testa .
Il lavoratore viene creato nel thread principale, e quindi i suoi eventi vengono elaborati nel thread principale. È necessario spostare il lavoratore nel proprio thread:
Worker worker;
worker.moveToThread(&worker);
worker.start();
Ora Qt conosce worker
che vive nel nuovo thread e metterà in coda gli eventi in quel ciclo di eventi.
Sembra che il tuo thread di lavoro sia completo prima che tu possa persino invocare qualsiasi funzione o inviargli un segnale.