Qt定时器无法从另一个线程停止。

15

您好,

我正在编写我的第一个Qt程序,遇到了以下问题:

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

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

我的程序将与CANOpen总线通信,我使用Canfestival Stack来实现。Canfestival将使用回调方法工作。为检测通信超时,我设置了一个定时器函数(类似于看门狗)。我的计时器包括“tmr”模块、“TimerForFWUpgrade”模块和“SingleTimer”模块。“tmr”模块最初是C语言编写的,因此静态的“TimerForFWUpgrade”方法将与其进行接口处理。 “tmr”模块将成为C语言编写的固件更新包的一部分。

计时器将按以下方式工作。在发送消息之前,我将调用TMR_Set方法。然后,在我的空闲程序循环中,使用TMR_IsElapsed检查定时器下溢。如果TMR_IsElapsed,则进行错误处理。如您所见,TMR_Set方法将不断地被调用并重启QTimer。

如果我启动程序,上述错误将出现。您能告诉我我的概念是否可行吗?为什么会出现这些错误?我是否需要使用额外的线程(QThread)来处理主线程?

谢谢,
Matt

运行和空闲循环:

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
    ....
}  

模块 tmr(与 C 程序接口):

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

INT8 TMR_IsElapsed(UINT8 tmrnbr)
{
 return TimerForFWUpgrade::isElapsed(tmrnbr);
}
}

模块 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;
    }
}

模块 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;
}

4
你的开销很大。为什么不使用QTimer类呢? - Dmitry Sazonov
@UmNyobe,你能给我更多的信息吗?isElapsed函数体里有什么问题?谢谢。 - Matt
@DmitrySazonov 我使用QTimer类。但我不能直接使用它,或者也许你可以告诉我如何使用... - Matt
为什么你不能直接使用它?你能展示一下你的尝试吗? - Dmitry Sazonov
调用 QCoreApplication::processEvents();。它不应该在那里。你可能需要在 run() 的 while 循环中使用它,但它现在不应该在那里。 - UmNyobe
3个回答

8
你遇到这个问题是因为静态数组中的定时器被创建在线程X,但是在线程Y中启动和停止。这是不允许的,因为Qt依赖于线程关联来超时定时器。
你可以在同一个线程中创建、启动和停止,或者使用信号和槽来触发定时器的startstop操作。信号和槽的解决方案有点棘手,因为你有n个QTimer对象(提示:你如何在位置i开始计时器?)。
相反,你可以在tmrnbr位置创建和初始化定时器。
TimerForFWUpgrade::set(unsigned char tmrnbr, unsigned int time)
{
     singleTimer[tmrnbr] = new SingleTimer(0);
     singleTimer[tmrnbr]->set(time);
}

该代码由同一线程执行。

此外,您不需要一个SingleTimer类。您正在使用Qt5,并且已经拥有所需的所有工具:

  • SingleTimer::isElapsed实际上是QTimer::remainingTime() == 0
  • SingleTimer::set实际上是QTimer::setSingleShot(true); QTimer::start(time);
  • SingleTimer::slot_setElapsed变得无用
  • 因此,SingleTimer::SingleTimer变得无用,您不再需要SingleTimer

8
使用QTimer来实现此目的,并利用SIGNALSSLOT从不同的线程启动和停止计时器。您可以从任何线程发出信号,并在创建计时器的线程中捕获它以对其进行操作。
既然您说您是Qt新手,我建议您先阅读一些教程,以便了解Qt提供了什么,并且不要试图重复造轮子。 :) VoidRealms 是一个很好的起点。

0

在更改我的计时器概念后,我解决了错误。我不再使用SingleTimer模块。使用QTimer之前,我不会让其超时,可能因此遇到问题。现在我有一个循环的QTimer,在slot函数中以100ms的间隔超时,然后我将计算事件数。以下是我的工作代码:

TimerForFWUpgrade::TimerForFWUpgrade(QObject* parent) : QObject(parent),
                                                        pTime(new QTimer(this))
{
    connect(pTime, SIGNAL(timeout()), this, SLOT(slot_handleTimer()));
    pTime->setTimerType(Qt::PreciseTimer);
    pTime->start(100);
}

void TimerForFWUpgrade::set(unsigned char tmrnbr, unsigned int time)
{
    if(tmrnbr < NR_OF_TIMERS)
    {
        if(timeBase != 0)
        {
            myTimeout[tmrnbr] = time / timeBase;
        }
        else
        {
            myTimeout[tmrnbr] = 0;
        }
        myTimer[tmrnbr] = 0;
        myElapsed[tmrnbr] = false;
        myActive[tmrnbr] = true;
    }

}

char TimerForFWUpgrade::isElapsed(unsigned char tmrnbr)
{
    QCoreApplication::processEvents();
    if(tmrnbr < NR_OF_TIMERS)
    {
        if(true == myElapsed[tmrnbr])
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
    else
    {
        return 0; // NOK
    }
}

void TimerForFWUpgrade::slot_handleTimer()
{
    for(UINT8 i = 0; i < NR_OF_TIMERS; i++)
    {
        if(myActive[i] == true)
        {
            myTimer[i]++;
            if(myTimeout[i] < myTimer[i])
            {
                myTimer[i] = 0;
                myElapsed[i] = true;
                myActive[i] = false;
            }
         }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接