QTimer isActive возвращает истину, но оставшееся время возвращает -1

Таймер определен и запущен в начале:

QTimer *teleTimer;
teleTimer = new QTimer();
QObject::connect(teleTimer, SIGNAL(timeout()), this, SLOT(somefunction1())); 
teleTimer->start(200);

Потом где-то останавливается и вызывается другая функция somefunction2(). После завершения этой функции таймер снова запускается:

if (teleTimer->isActive())
{
    qDebug() << teleTimer->remainingTime();
    teleTimer->stop();
    delete teleTimer;
}
teleTimer = new QTimer();
QObject::connect(teleTimer, SIGNAL(timeout()), this, SLOT(somefunction3())); 
teleTimer->start(200);

Однако teleTimer->isActive() возвращает true, а teleTimer->remainingTime() возвращает -1, тогда приложение аварийно завершает работу:

Thread 2 Crashed:: QThread
0   org.qt-project.QtCore           0x000000010ed4be3b QObject::killTimer(int) + 27
1   org.qt-project.QtCore           0x000000010ed5a9b9 QTimer::stop() + 25
2   com.yourcompany.QTGCS           0x000000010dffa609 TelemetrySerialWorker::setTelemetryMode(int) + 745 (telemetryserialworker.cpp:114)

Итак, как это исправить? Спасибо.

Обновление: эта проблема решена. Спасибо за все отклики. В следующий раз постараюсь задать вопрос в хорошем формате. Спасибо.


person Tairan Liu    schedule 06.05.2018    source источник
comment
изменить delete teleTimer() на delete teleTimer->deleteLater();   -  person eyllanesc    schedule 06.05.2018
comment
@eyllanesc Вы имеете в виду изменение на teleTimer->deleteLater();? Я пробовал, но все равно вылетает. Я думаю, что проблема исходит от teleTimer->stop(), а не delete.   -  person Tairan Liu    schedule 06.05.2018
comment
@eyllanesc Возможно, вы правы ... Позвольте мне провести еще тесты.   -  person Tairan Liu    schedule 06.05.2018
comment
Возможно, это состояние гонки, поскольку в соответствии с трассировкой стека задействовано несколько потоков. Можете ли вы создать минимальный, полный и проверяемый пример, воспроизводящий сбой?   -  person MrEricSir    schedule 06.05.2018
comment
Просто из интереса, почему вы удаляете таймер? Кажется, что вы должны просто остановить его и запустить, не удаляя. В этом случае вам даже не нужно повторно подключаться.   -  person paxdiablo    schedule 07.05.2018
comment
@paxdiablo Это потому, что в моем коде я фактически подключался к разным слотам. Я не показал этого в примере выше.   -  person Tairan Liu    schedule 07.05.2018
comment
@paxdiablo На самом деле это somefunction1(), somefunction2(), somefunction3(), ... . Я не вводил. Я использую таймер с разными интервалами. Но ваш комментарий имеет смысл. Это не обязательно. Отличный совет.   -  person Tairan Liu    schedule 07.05.2018
comment
Ах, теперь это имеет больше смысла. Однако вам по-прежнему не нужно удалять и создавать заново, вы можете просто отключиться и подключиться к новому слоту. Причина, по которой я упоминаю об этом, заключается в том, что иногда обратный вызов таймера выполняется во время удаления, что вызывает эти проблемы. Disc/conn не приведет к выдергиванию ковра из-под обратных вызовов в полете. Тем не менее, вы решили это, поэтому нет необходимости продолжать, я просто подумал, что упомяну свои рассуждения.   -  person paxdiablo    schedule 07.05.2018
comment
@paxdiablo Да, ты прав. Это имеет смысл. Отличный совет.   -  person Tairan Liu    schedule 07.05.2018
comment
Это очень плохой вопрос: пожалуйста, создайте автономный пример одного файла, который показывает, как вы используете эти несколько функций. Вы, скорее всего, немного все усложняете. Этот вопрос относится к проекту QtGCS? Если да, пожалуйста, покажите, что именно в коде вы пытаетесь исправить.   -  person Kuba hasn't forgotten Monica    schedule 07.05.2018
comment
@KubaOber Спасибо за ваш комментарий. Постараюсь в следующий раз сделать как вы предложили.   -  person Tairan Liu    schedule 07.05.2018


Ответы (2)


Вообще говоря, если вы вызываете функции в последовательности, проще всего сохранить последовательность в таблице и использовать однократные таймеры. Воссоздание таймеров или подключений стоит дорого.

#include <QtCore>
#include <functional>
#include <initializer_list>

class Sequencer : public QObject {
public:
  using base_class = QObject;
  struct Element {
    int delay = 0;
    std::function<void()> functor;
  };
  explicit Sequencer(QObject *parent = {}) : QObject(parent) {}
  // takes care of initializer list and other compatible initializers
  Sequencer(QVector<Element> init, QObject *parent = {}) : 
    QObject(parent), m_sequence(std::move(init)) {}
  void start(int index = 0) {
    m_it = m_sequence.begin() + index;
    m_timer.start(m_it->delay, this);
  }
  int stop() {
    m_timer.stop();
    return m_it - m_sequence.begin();
  }
protected:
  void timerEvent(QTimerEvent *event) override {
    if (m_timer.timerId() != event->timerId())
      return base_class::timerEvent(event);
    m_it->functor();
    m_it++;
    if (m_it != m_sequence.end())
      m_timer.start(m_it->delay, this);
    else
      m_timer.stop();
  }
private:
  QVector<Element> m_sequence;
  QVector<Element>::const_iterator m_it = m_sequence.begin();     
  QBasicTimer m_timer;
};

Вы бы использовали его так, например:

class MyClass { // can be QObject, but doesn't have to be
  Sequence m_seq{
    {200, [=]{ function1(); }},   // after 200ms delay
    {0,   [=]{ function2(); }},   // after no delay
    {500, [=]{ function3(); }},   // after 500ms delay
    {0,   [=]{ loop(); }}};       // and right away loop the sequence
  void function1();
  void function2();
  void function3();
  void loop() { m_seq.start(); }
public:
  MyClass() {
    m_seq.start();
  }
};
person Kuba hasn't forgotten Monica    schedule 07.05.2018
comment
Спасибо за ответ. Я никогда не использовал метод, который вы упомянули раньше. Мне кажется интересным, буду пробовать. Все функции вызываются более одного раза, например, изначально somefunction1() вызывается каждые 200 мс, затем, если нет взаимодействия с GUI, он будет бесконечно вызывать эту функцию каждые 200 мс. Затем, если какая-либо кнопка будет нажата, она перейдет к somefunction2() или другим. - person Tairan Liu; 07.05.2018

Благодаря подсказке @eyllanesc моя проблема наконец-то решена.

В самом начале я использовал delete teleTimer, потом моя программа начала падать. В то время я думал, что причина в том, что таймер действительно не остановился к тому времени, когда эта функция снова вызвала. Затем я изменил свой код, чтобы запустить еще один таймер одиночного выстрела, чтобы у него действительно был таймер для остановки.

Самое начало: (разбился)

void timerSwitch()
{
    if (teleTimer->isActive())
    {
        qDebug() << teleTimer->remainingTime();
        teleTimer->stop();
        delete teleTimer;
    }
    if (mode == 1)
    {
        teleTimer = new QTimer();
        QObject::connect(teleTimer, SIGNAL(timeout()), this, SLOT(somefunction())); 
        teleTimer->start(200);
    }
    else if (mode == 2)
    {
        somefunction2();
    }

}

Первое изменение: (разбился)

void timerSwitch()
{
    if (teleTimer->isActive())
    {
        qDebug() << teleTimer->remainingTime();
        teleTimer->stop();
        delete teleTimer;
    }
    if (mode == 1)
    {
        teleTimer = new QTimer();
        QObject::connect(teleTimer, SIGNAL(timeout()), this, SLOT(somefunction())); 
        teleTimer->start(200);
    }
    else if (mode == 2)
    {
        teleTimer = new QTimer();
        teleTimer.singleshot(..., ..., SLOT(somefunction2()));
    }

}

Затем с подсказкой, данной @eyllanesc, я изменил свой код на:

void timerSwitch()
{
    if (teleTimer->isActive())
    {
        qDebug() << teleTimer->remainingTime();
        teleTimer->stop();
        teleTimer->deleteLater();
    }
    if (mode == 1)
    {
        teleTimer = new QTimer();
        QObject::connect(teleTimer, SIGNAL(timeout()), this, SLOT(somefunction())); 
        teleTimer->start(200);
    }
    else if (mode == 2)
    {
        teleTimer = new QTimer();
        teleTimer.singleshot(..., ..., SLOT(somefunction2()));
    }

}

Моя программа все еще зависала.

Затем я изменил свой код на:

void timerSwitch()
{
    if (teleTimer->isActive())
    {
        qDebug() << teleTimer->remainingTime();
        teleTimer->stop();
        teleTimer->deleteLater();
    }
    if (mode == 1)
    {
        teleTimer = new QTimer();
        QObject::connect(teleTimer, SIGNAL(timeout()), this, SLOT(somefunction())); 
        teleTimer->start(200);
    }
    else if (mode == 2)
    {
        somefunction2();
    }

}

На этот раз моя программа работает нормально. Протестировал более 20 раз, все работает корректно и не зависает.

В процессе все причины, выдаваемые моим компьютером, аналогичны этой:

Thread 2 Crashed:: QThread
0   org.qt-project.QtCore           0x000000010ed4be3b QObject::killTimer(int) + 27
1   org.qt-project.QtCore           0x000000010ed5a9b9 QTimer::stop() + 25
2   com.yourcompany.QTGCS           0x000000010dffa609 TelemetrySerialWorker::setTelemetryMode(int) + 745 (telemetryserialworker.cpp:114)

Я думал, что ошибка возникла из-за stop(), так как isActive() возвращает true, а remainingTime() возвращает -1. Насколько я понимаю, когда был вызван isActive(), таймер действительно каким-то образом не был остановлен, но когда был вызван remainingTime(), таймер был остановлен. Тогда я почувствовал, что это не должно быть так глупо. У меня все еще есть сомнения, но моя проблема решена. Я обновлю более подробную информацию, когда у меня будет время. Спасибо всем откликнувшимся мне.

person Tairan Liu    schedule 07.05.2018