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

Я работаю над приложением Qt. Там я использую два потока, один для графического интерфейса и один для обработки.

У меня есть рабочий класс, в котором QTimer является классом-членом.

.h файл:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTimer>
#include <QThread>

class Worker : public QObject
{
  Q_OBJECT
 public:
  Worker();
  QTimer t;
 public slots:
  void process();
  void startWorker();
};

namespace Ui {
 class MainWindow;
}

class MainWindow : public QMainWindow
{
  Q_OBJECT

  public:
   explicit MainWindow(QWidget *parent = nullptr);
   ~MainWindow();

 private:
   QThread workerThread;
   Worker wt;
 };

 #endif // MAINWINDOW_H

cpp-файл

#include "mainwindow.h"
#include <QDebug>
#include <iostream>

Worker::Worker() : t(this)
{
 connect(&t, SIGNAL(timeout()), this, SLOT(process()));
}

void Worker::process()
{
  std::cout << "triggering timer" << std::endl;
}

void Worker::startWorker()
{
  t.start(1000);
}

MainWindow::MainWindow(QWidget *parent) :
  QMainWindow(parent)
{
  wt.moveToThread(&workerThread);
  qDebug() << "worker thread " << wt.thread();
  qDebug() << "timer thread " << wt.t.thread();
  connect(&workerThread, SIGNAL(started()), &wt, SLOT(startWorker()));
  connect(&workerThread, &QThread::finished, &workerThread, &QObject::deleteLater);
  workerThread.start();
}

MainWindow::~MainWindow()
{
 workerThread.quit();
 workerThread.wait();
}

Я могу начать тему без ошибок. Однако, когда я закрываю приложение, я получаю предупреждающее сообщение.

QObject::killTimer: Timers cannot be stopped from another thread 
QObject::~QObject: Timers cannot be stopped from another thread

Если QTimer является дочерним элементом рабочего класса и был перемещен в поток, почему Qt жалуется на его остановку из другого потока? Примечание. Я добавил журналы для печати идентификатора потока, и в обоих случаях он выводит одинаковое значение:

worker thread  QThread(0x72fdf0)
timer thread  QThread(0x72fdf0)

Может кто-нибудь объяснить? Я не понимаю, что здесь происходит

заранее спасибо


person RuLoViC    schedule 13.11.2018    source источник
comment
пожалуйста, предоставьте минимальный воспроизводимый пример :-)   -  person eyllanesc    schedule 13.11.2018
comment
что это за предупреждающее сообщение?   -  person eyllanesc    schedule 14.11.2018
comment
@eyllanesc QObject::killTimer: таймеры нельзя остановить из другого потока QObject::~QObject: таймеры нельзя остановить из другого потока   -  person RuLoViC    schedule 14.11.2018
comment
Я рекомендую поместить это сообщение в свой вопрос   -  person eyllanesc    schedule 14.11.2018
comment
Таймер останавливается при уничтожении, а не при завершении потока. Поскольку рабочий является членом вашего главного окна, это происходит, как только происходит выход из основного окна, что приводит к этой ошибке.   -  person Felix    schedule 14.11.2018
comment
@RuLoViC Очевидно, я протестировал его, вы можете найти мой тестовый код по следующей ссылке github.com/eyllanesc/stackoverflow/tree/master/questions/ . Может быть, я забыл что-то указать, попробуйте и дайте мне знать.   -  person eyllanesc    schedule 14.11.2018
comment
Я скопировал ваш код. Это выходной сигнал запуска таймера. QObject::killTimer: Таймеры не могут быть остановлены из другого потока. Ошибка ASSERT в QCoreApplication::sendEvent: Не удается отправить события объектам, принадлежащим другому потоку. Текущий поток 0x0x1f9a0580. Receiver '' (типа 'Worker') был создан в потоке 0x0x72fdf0, файл kernel\qcoreapplication.cpp, строка 576.   -  person RuLoViC    schedule 14.11.2018
comment
@RuLoViC Ну, в Qt 5.11.2 работает правильно, может мое решение правильно только для текущих версий, какая у вас версия Qt?   -  person eyllanesc    schedule 14.11.2018
comment
5.11.2. Я тестировал на Windows и Mac   -  person RuLoViC    schedule 14.11.2018
comment
Не могли бы вы попробовать мой код? github.com/rulovic/TimersAndThreads/tree/master/ThreadTimer/   -  person RuLoViC    schedule 14.11.2018


Ответы (2)


Наконец-то я смог исправить ошибку:

  1. Преобразование QTimer в указатель
  2. Добавление слота stopWorker, предложенное @Amfasis
  3. В этом слоте не только остановить QTimer, но и удалить его

Спасибо всем

person RuLoViC    schedule 14.11.2018
comment
Если вы создаете экземпляр QTimer с указателем на своего рабочего (как родителя), я бы сказал, что вам не нужно удалять. Также не забудьте снова создать экземпляр, если вы снова запустите работника. - person Amfasis; 14.11.2018

Вы должны остановить таймер до того, как QObject удалит его сам

в файле .h добавьте деструктор:

class Worker : public QObject
{
    Q_OBJECT
public:
    Worker();
    ~Worker();
private:
    QTimer t;
public slots:
    void process();
    void startWorker();
    void stopWorker(); //this line was added
};

в файле .cpp добавьте:

Worker::stopWorker()
{
    t.stop();
}

и в конструкторе

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    ...
    connect(&workerThread, &QThread::finished, &wt, &Worker::stopWorker); //add this line!
    ...
}
person Amfasis    schedule 14.11.2018