Qt timers cannot be stopped from another thread

29,202

Solution 1

Use QTimer for this purpose and make use of SIGNALS and SLOT for the purpose of starting and stopping the timer/s from different threads. You can emit the signal from any thread and catch it in the thread which created the timer to act on it.

Since you say you are new to Qt, I suggest you go through some tutorials before proceeding so that you will know what Qt has to offer and don't end up trying to reinvent the wheel. :)

VoidRealms is a good starting point.

Solution 2

You have this problem because the timers in the static array is created in Thread X, but started and stopped in Thread Y. This is not allowed, because Qt rely on thread affinity to timeout timers.

You can either create, start stop in the same thread or use signal and slots to trigger start and stop operations for timers. The signal and slot solution is a bit problematic Because you have n QTimer objects (Hint: how do you start the timer at position i?)

What you can do instead is create and initialize the timer at position tmrnbr in

TimerForFWUpgrade::set(unsigned char tmrnbr, unsigned int time)
{
     singleTimer[tmrnbr] = new SingleTimer(0);
     singleTimer[tmrnbr]->set(time);
}

which is executed by the same thread.

Futhermore, you don't need a SingleTimer class. You are using Qt5, and you already have all you need at your disposal:

  • SingleTimer::isElapsed is really QTimer::remainingTime() == 0;
  • SingleTimer::set is really QTimer::setSingleShot(true); QTimer::start(time);
  • SingleTimer::slot_setElapsed becomes useless
  • ThusSingleTimer::SingleTimer becomes useless and you dont need a SingleTimer class anymore
Share:
29,202

Related videos on Youtube

Matt
Author by

Matt

Updated on July 11, 2020

Comments

  • Matt
    Matt almost 4 years

    Hy,

    I'm writing my first Qt program and getting now in troubles with:

    QObject::killTimer: timers cannot be stopped from another thread

    QObject::startTimer: timers cannot be started from another thread

    My program will communicate to a CANOpen bus for that I'm using the Canfestival Stack. The Canfestival will work with callback methods. To detects timeout in communication I setup a timer function (somehow like a watchdog). My timer package consist out of a "tmr" module, a "TimerForFWUpgrade" module and a "SingleTimer" module. The "tmr" module was originally C programmed so the static "TimerForFWUpgrade" methods will interface it. The "tmr" module will be part of a C programed Firmware update package.

    The timer will work as follows. Before a message is sent I will call TMR_Set method. An then in my idle program loop with TMR_IsElapsed we check for a timer underflow. If TMR_IsElapsed I will do the errorhandling. As you see the TMR_Set method will be called continuously and restart the QTimer again and again.

    The above noted errors are appearing if I start my program. Can you tell me if my concept could work? Why does this errors appear? Do I have to use additional threads (QThread) to the main thread?

    Thank you

    Matt

    Run and Idle loop:

    void run
    {
        // start communicate with callbacks where TMR_Set is set continously
        ...
    
        while(TMR_IsElapsed(TMR_NBR_CFU) != 1);
    
        // if TMR_IsElapsed check for errorhandling
        ....
    }  
    

    Module tmr (interface to C program):

    extern "C"
    {
    void TMR_Set(UINT8 tmrnbr, UINT32 time)
    {
        TimerForFWUpgrade::set(tmrnbr, time);
    }
    
    INT8 TMR_IsElapsed(UINT8 tmrnbr)
    {
     return TimerForFWUpgrade::isElapsed(tmrnbr);
    }
    }
    

    Module TimerForFWUpgrade:

    SingleTimer* TimerForFWUpgrade::singleTimer[NR_OF_TIMERS];
    
    TimerForFWUpgrade::TimerForFWUpgrade(QObject* parent)
    {
    
        for(unsigned char i = 0; i < NR_OF_TIMERS; i++)
        {
            singleTimer[i] = new SingleTimer(parent);
        }
    }
    
    //static
    void TimerForFWUpgrade::set(unsigned char tmrnbr, unsigned int time)
    {
        if(tmrnbr < NR_OF_TIMERS)
        {
            time *= TimerForFWUpgrade::timeBase;
            singleTimer[tmrnbr]->set(time);
        }
    
    }
    
    
    //static
    char TimerForFWUpgrade::isElapsed(unsigned char tmrnbr)
    {
        if(true == singleTimer[tmrnbr]->isElapsed())
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
    

    Module SingleTimer:

    SingleTimer::SingleTimer(QObject* parent) : QObject(parent),
                                                pTime(new QTimer(this)),
                                                myElapsed(true)
    {
        connect(pTime, SIGNAL(timeout()), this, SLOT(slot_setElapsed()));
        pTime->setTimerType(Qt::PreciseTimer);
        pTime->setSingleShot(true);
    }
    
    void SingleTimer::set(unsigned int time)
    {
        myElapsed = false;
        pTime->start(time);
    }
    
    bool SingleTimer::isElapsed()
    {
        QCoreApplication::processEvents();
        return myElapsed;
    }
    
    void SingleTimer::slot_setElapsed()
    {
        myElapsed = true;
    }
    
    • Dmitry Sazonov
      Dmitry Sazonov over 9 years
      You have great overhead. Why you don't use QTimer class?