Bonjour,
Le premier exemple de cette doc encapsule un object QThread dans une classe Controller.
Du coup, ~Controller doit appeler wait() pour s'assurer que le thread est terminé, sans quoi le QThread serait détruit trop top:
Sur la même page de documentation, quelques § plus tard:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 class Worker : public QObject { Q_OBJECT public slots: void doWork(const QString ¶meter) { QString result; /* ... here is the expensive or blocking operation ... */ emit resultReady(result); } signals: void resultReady(const QString &result); }; class Controller : public QObject { Q_OBJECT QThread workerThread; public: Controller() { Worker *worker = new Worker; worker->moveToThread(&workerThread); connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater); connect(this, &Controller::operate, worker, &Worker::doWork); connect(worker, &Worker::resultReady, this, &Controller::handleResults); workerThread.start(); } ~Controller() { workerThread.quit(); workerThread.wait(); } public slots: void handleResults(const QString &); signals: void operate(const QString &); };
Je suis totalement d'accord avec ça: si j'utilise un thread, c'est probablement pour ne pas freezer l'UI. Appeler wait() dans ~Controller() pourrait la freezer, donc je pense que c'est une très mauvaise idée.Note: wait() and the sleep() functions should be unnecessary in general, since Qt is an event-driven framework. Instead of wait(), consider listening for the finished() signal.
Par conséquent, je pense qu'il vaut mieux utiliser un QThread* dans Controller, et connecter &QThread::finished à &QObject::deleteLater à la fois pour le worker et le workerThread:
Ainsi, le destructeur du Controller peut terminer sans attendre (sans wait()), et le worker/workerThread seront supprimés dès que possible. Et ça fonctionne bien…
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 class Controller : public QObject { Q_OBJECT QThread *workerThread; public: Controller() : workerThread(new QThread) { Worker *worker = new Worker; worker->moveToThread(workerThread); connect(workerThread, &QThread::finished, worker, &QObject::deleteLater); connect(workerThread, &QThread::finished, workerThread, &QObject::deleteLater); connect(this, &Controller::operate, worker, &Worker::doWork); connect(worker, &Worker::resultReady, this, &Controller::handleResults); workerThread->start(); } ~Controller() { workerThread->quit(); // no need to workerThread->wait(); } public slots: void handleResults(const QString &); signals: void operate(const QString &); };
Sauf sur la terminaison du programme. Si le destructeur du Controller est dû à la terminaison du programme, alors l'event loop est en train d'être stoppée, et les slots [i]deleteLater[/] ne seront jamais appelés.
Nous pourrions dire "ce n'est pas grave, le programme se termine de toute façon", mais ça semble insatisfant. En théorie, nous pourrions quand même vouloir exécuter les destructeurs du worker/workerThread à la fin de programme (et ça éviterait à valgrind de rapporter beaucoup de fuites mémoire à cause de ça).
Qu'en pensez-vous?
Partager