Witam!
Stworzyłem przykładową klasę, gdzie pętla wykonuje się w wątku:
// idea wątku z nieskończoną pętlą.
#ifndef QWORKER_H
#define QWORKER_H
#include <QObject>
#include <QThread>
#include <QEventLoop>
#include <QTimer>
#include <QMutex>
#include <QDebug>
class QWorker : public QObject
{
Q_OBJECT
public:
explicit QWorker(QObject *parent = 0) :
//QObject(parent), // będziesz sierotą
QObject(),
mThread(),
mTerminate(false)
{
qDebug() << "Parent thread" << parent->thread()->currentThreadId();
this->moveToThread(&mThread);
connect(&mThread, SIGNAL(started()), this, SLOT(doWork()));
mThread.start();
}
~QWorker(){
qDebug() << __FUNCTION__ << thread()->currentThreadId();
abort();
mThread.quit();
mThread.wait();
}
void abort(){
QMutexLocker ml(&mMutex);
mTerminate = true;
}
private:
QThread mThread;
bool mTerminate;
QMutex mMutex;
/* ... */
signals:
public slots:
void doWork(){
qDebug() << __FUNCTION__ << "enter";
forever{
mMutex.lock();
if(mTerminate){
qDebug() << __FUNCTION__ << "Terminate";
break;
}
mMutex.unlock();
/* ... */
qDebug() << __FUNCTION__ << "looping" << thread()->currentThreadId();
#if 1
QEventLoop loop;
QTimer::singleShot(1000, &loop, SLOT(quit()));
loop.exec();
#else
mThread.sleep(1);
#endif
}
mMutex.unlock();
qDebug() << __FUNCTION__ << "exit";
}
void doMethod(){
QMutexLocker ml(&mMutex);
/* ... */
qDebug() << __FUNCTION__ << thread()->currentThreadId();
}
};
#endif // QWORKER_H
Tworzenie oraz testy robię tak:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
mWorker = new QWorker(this);
connect(ui->pushButton_1, SIGNAL(clicked()), mWorker, SLOT(doMethod()));
connect(ui->pushButton_2, SIGNAL(clicked()), mWorker, SLOT(doMethod()), Qt::DirectConnection);
}
MainWindow::~MainWindow()
{
delete mWorker;
delete ui;
}
Mocno nie chciałbym w MainWindow zawierać kodu który wrzuca "Workera" do wątku, więc zaimplementowałem to w konstruktorze Workera.
Działanie:
Parent thread 0x7f922da7f780
Object // klasa opisana w dalszej części tematu
doWork enter
doWork looping 0x7f9221646700
doWork looping 0x7f9221646700
doWork looping 0x7f9221646700
doMethod 0x7f922da7f780 // ui->pushButton_2 - Qt::DirectConnection
doMethod 0x7f9221646700 // ui->pushButton_1 - a tego się nie spodziewałem ;) Fajnie ;)
doWork looping 0x7f9221646700
~QWorker 0x7f922da7f780
doWork Terminate
doWork exit
~Object
Moje pytania
1. Jak wywołać destruktora Workera przy zamykaniu aplikacji?
Oczywiście z pozbyciem się:
MainWindow::~MainWindow()
{
//delete mWorker; // !!!!!!!!!!!
delete ui;
}
Podoba mi się takie "inteligentne" niszczenie obiektów:
#ifndef OBJECT_H
#define OBJECT_H
#include <QObject>
#include <QDebug>
class Object : public QObject
{
Q_OBJECT
public:
explicit Object(QObject *parent = 0) :
QObject(parent)
{
qDebug() << __FUNCTION__;
}
~Object(){
qDebug() << __FUNCTION__;
}
signals:
public slots:
};
#endif // OBJECT_H
Gdzie utworzenie za pomocą:
new Object(this);
przechowuje informację, że jeżeli rodzic zginie, dzieciaki też poumierają:
# start aplikacji
Object
# kliknięcie X aplikacji ;)
~Object
Worker nie może mieć rodzica, ponieważ jest przenoszony do nowego wątku, co więc zrobić?
2. Czy klasa QWorker jest poprawnie napisana?
-czy QWorker jest dobrze zabijany?
-czy QThread jest poprawnie zamykany?
3. Czy obie formy są poprawne w wątku?
#if 1
QEventLoop loop;
QTimer::singleShot(1000, &loop, SLOT(quit()));
loop.exec();
#else
mThread.sleep(1);
#endif
Nie będę tego stosował (ponieważ w wątku będzie odpalona niskopoziomowa biblioteka), ale warto wiedzieć ;)
4. Jak to się dzieje, że metoda doWork pracuje w wątku QWorker
Jeżeli wykonam sygnał z przycisku pushButton_1, mam wynik:
doMethod 0x7f4744dbb700
doWork looping 0x7f4744dbb700
Rozumiem, że jeśli w nieskończonej pętli mam jakieś wytchnienie w postaci sleepa, to QThread ma szanse pozbierać z kolejki żądania i powywoływać sloty w Workerze, tak?
Jeśli zapewnie, że metody będą wykonywane na tym samym wątku co "doWork", to mogę wywalić mutex'y?
Jeśli nieskończona pętla nie będzie dawać wytchnienia, to metody muszą być wykonywane bezpośrednio (Qt::DirectConnection) ?
P.S.
Staram się zrozumieć wątki w QT tak aby nie dziedziczyć po "run", bo krzyczycie na mnie i dostrzegam sens ;)
Chciałbym się upewnić że wszystko w 100% rozumiem.