Правильный способ очистки объекта с помощью QTimer в стеке уничтоженного (законченного) QThread - PullRequest
0 голосов
/ 17 января 2020

Идея в наброске - у меня есть _object с _timer в стеке (переставлен) и _thread для них в gui:

#include <QTimer>
#include <QObject>
#include <QMainWindow>
#include <QThread>
#include <QSharedPointer>

#include "appguilogcategories.h"

class SomeClass final : public QObject
{
    Q_OBJECT
public:
    SomeClass(QObject *parent = nullptr) { /* create _inner, set parent 'this' for them ... */ };
private:
    QSharedPointer<SomeInnerClass> _inner;
};

class SomeInnerClass : public QObject
{
    Q_OBJECT
public:
    SomeInnerClass(QObject *parent = nullptr) : QObject(parent)
    {
        _timer.setParent(this);
        _timer.start();
    }
    ~SomeInnerClass() { _timer.stop(); }
private:
    QTimer _timer;
};

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr)
    {
        _ui->setupUi(this);
        setThreadUp();
    }
    ~MainWindow()
    {
        bool is_thread_active = !_thread.isNull()
            && _thread->isRunning()
            && !_thread->isFinished();
        if (is_thread_active) {
            _thread->quit();
            if (!_thread->wait(200)) {
                qCCritical(gui()).noquote() << tr("fuck with it!");
                _thread->terminate();
                _thread->wait();
            }
        }
        delete _ui;
    }
private:
    Ui::MainWindow *_ui;
    SomeClass _object;
    QSharedPointer<QThread> _thread;
    int setThreadUp()
    {
        _thread = QSharedPointer<QThread>(new QThread());
        if (_thread.isNull()) {
            qCCritical(gui()).noquote() << tr("memory for thread allocation failed!");
            return EXIT_FAILURE;
        }
        _object.moveToThread(_thread.data());
        Qt::ConnectionType ct = static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection);
        connect(_thread.data(), &QThread::finished , &_object, &SomeObject::deleteLater, ct);
        connect(_thread.data(), &QThread::finished , [](){ qCDebug(gui()).noquote() << tr("thread finished"); });
        _thread->start();
        return EXIT_SUCCESS;
    }
}

Проблема в том, что _thread сначала уничтожается при закрытии окна, а затем _object & _inner уничтожается позже с остановкой _timer. Но _inner меняет свой поток на 0x0 после _thread окончательной печати и печати отладчика:

QObject :: killTimer: Таймеры нельзя остановить из другого потока

QObject: : ~ QObject: Таймеры не могут быть остановлены из другого потока

Итак, как правильно очистить QObject с QTimer в глубине в QThread ИЛИ как уничтожить QObject до QThread ИЛИ Реорганизовать этот эскиз?

PS: SomeClass - контроллер устройства (рабочий), SomeInnerClass - контроллер порта, QTimer - контролер пакетов. При закрытии окна отключите устройство с контроллером уничтожения.

...