C++ Qt 计时器不能从另一个线程停止

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26461887/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 11:36:36  来源:igfitidea点击:

Qt timers cannot be stopped from another thread

c++multithreadingqtqthreadqtimer

提问by Matt

Hy,

嗨,

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

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

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

QObject::killTimer:定时器不能从另一个线程停止

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

QObject::startTimer:定时器不能从另一个线程启动

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.

我的程序将与CANOpen总线通信,因为我正在使用Canfestival Stack。Canfestival 将使用回调方法。为了检测通信超时,我设置了一个计时器功能(有点像看门狗)。我的计时器包由“tmr”模块、“TimerForFWUpgrade”模块和“SingleTimer”模块组成。“tmr”模块最初是用 C 语言编程的,因此静态“TimerForFWUpgrade”方法将连接它。“tmr”模块将是 C 编程固件更新包的一部分。

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.

定时器将按如下方式工作。在发送消息之前,我将调用 TMR_Set 方法。然后在我的空闲程序循环中使用 TMR_IsElapsed 我们检查计时器下溢。如果 TMR_IsElapsed 我会做错误处理。如您所见,TMR_Set 方法将被连续调用并一次又一次地重新启动 QTimer。

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?

如果我启动我的程序,就会出现上述错误。你能告诉我我的概念是否可行吗?为什么会出现这个错误?我是否必须在主线程中使用附加线程(QThread)?

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):

模块 tmr(C 程序接口):

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

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

Module TimerForFWUpgrade:

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

回答by UmNyobe

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.

您遇到此问题是因为静态数组中的计时器是在 中创建的Thread X,但在 中启动和停止Thread Y。这是不允许的,因为 Qt 依赖于超时计时器的线程关联。

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

您可以在同一线程中创建、启动停止或使用信号和插槽来触发startstop操作计时器。信号槽解决方案有点问题 因为你有nQTimer 对象(提示:你如何在位置启动计时器i?)

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

你可以做的反而是创建和初始化在位置计时器tmrnbr

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 SingleTimerclass. You are using Qt5, and you already have all you need at your disposal:

此外,您不需要SingleTimer上课。您正在使用 Qt5,并且您已经拥有了您所需要的一切:

  • SingleTimer::isElapsedis really QTimer::remainingTime() == 0;
  • SingleTimer::setis really QTimer::setSingleShot(true); QTimer::start(time);
  • SingleTimer::slot_setElapsedbecomes useless
  • ThusSingleTimer::SingleTimerbecomes useless and you dont need a SingleTimerclass anymore
  • SingleTimer::isElapsed是真的QTimer::remainingTime() == 0
  • SingleTimer::set是真的 QTimer::setSingleShot(true); QTimer::start(time);
  • SingleTimer::slot_setElapsed变得无用
  • 因此SingleTimer::SingleTimer变得无用,您SingleTimer不再需要课程

回答by nnb

Use QTimerfor this purpose and make use of SIGNALSand SLOTfor 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.

使用QTimer用于此目的和利用SIGNALS,并SLOT从不同的线程启动和停止计时器的目的/秒。您可以从任何线程发出信号并在创建计时器以对其进行操作的线程中捕获它。

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. :)

既然你说你是 Qt 的新手,我建议你在继续之前先阅读一些教程,这样你就会知道 Qt 必须提供什么,而不是最终试图重新发明轮子。:)

VoidRealmsis a good starting point.

VoidRealms是一个很好的起点。

回答by Matt

I got the errors away after changing my timer concept. I'dont use anymore my SingleTimer module. Before the QTimer I won't let timeout and maybe because of that I run into problems. Now I have a cyclic QTimer that times out every 100ms in slot function I will then count the events. Below my working code:

更改我的计时器概念后,我消除了错误。我不再使用我的 SingleTimer 模块。在 QTimer 之前,我不会让超时,也许正因为如此,我遇到了问题。现在我有一个循环 QTimer,它在槽函数中每 100 毫秒超时一次,然后我将计算事件。在我的工作代码下方:

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