【问题标题】:Strange undocumented QTimer/QEventLoop behaviour after the timer is manually restarted手动重新启动计时器后奇怪的未记录 QTimer/QEventLoop 行为
【发布时间】:2014-11-13 14:54:10
【问题描述】:

我最近在使用 QTimer 时偶然发现了这个,它调用了一个带有内部 QEventLoop 的函数

所以,假设我们有一个 QTimer 实例

QTimer* timer = new QTimer;

我们在构造函数的某个地方启动它,它每 100 毫秒开始计时一次

timer->start(100);

现在是有趣的部分,我们将它连接到具有内部 QEventLoop 的插槽

void SlotFunction()
{
    qDebug() << "entered";
    QEventLoop loop;
    loop.exec();
}

撇开这个循环有多愚蠢,我们看到我们永远不会完成对槽的处理,并且计时器的后续超时将继续堆积到执行队列中。一切都很好,应该是这样。

接下来是什么:因为 QEventLoop 确保我们的应用程序保持响应,而插槽无意识地闲置,我们可以制作一个按钮及其 clicked() 插槽,如下所示:

void OnClicked()
{
    timer->start(100);
}

我在这里所做的基本上是重新启动当前的计时器周期,仅此而已。正确的?没有!重启后,SlotFunction 再次触发,表明计时器重启后的滴答声实际上并不等于在它之前发出的所有其他滴答声...

我唯一的问题是:WTF?! 为什么手动重新启动计时器使其能够进入插槽额外时间?我在freenode上问过,但我得到的唯一答案是“应该是这样”

【问题讨论】:

    标签: qt qtimer qeventloop


    【解决方案1】:

    我试过了,每次点击都会创建另一个“输入”行。

    主事件循环无法处理另一个事件,因为我们被困在一个新的事件循环中。

    在实现第二个插槽并将此插槽连接到超时信号时,很容易看到这一点。

    • 当调用下一个事件循环并且不再处理任何排队的事件时,主事件循环会卡住。
    • 计时器本身也不会再排队任何事件,因为排队本身将在现在卡住的主事件循环中完成。计时器不在其自己的事件循环中运行(这就是 Qtimers 不是精确计时器的原因)。
    • 一旦点击按钮,新的事件循环就会检查计时器是否应该生成事件timeout()
    • 一旦处理了新事件,我们就会再次陷入另一个事件循环...
    • 这将一直持续到我们退出应用程序。
    • 退出应用程序时,我们看到循环反转并调用第二个插槽,就像我们单击按钮并跑到第一个插槽一样频繁

    代码:

    #include <QDebug>
    #include <QTime>
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
        timer = new QTimer;
        timer->setInterval(2000);
        connect(timer,SIGNAL(timeout()),this,SLOT(timerslot()));
        connect(timer,SIGNAL(timeout()),this,SLOT(timerslot2()));
        timer->start();
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::on_pushButton_clicked()
    {
        timer->start(2000);
    }
    
    void MainWindow::timerslot()
    {
        qDebug()<<"In";
        QEventLoop loop;
        loop.exec();
    }
    
    void MainWindow::timerslot2()
    {
        qDebug()<<"More";
    }
    

    开始时输出:

    In
    

    每次点击的输出:

    In
    

    点击 3 次后输出:

    In
    In
    In
    In
    

    退出应用程序的输出:

    In
    In
    In
    In
    More
    More
    More
    More
    

    【讨论】:

    • 谢谢,这就解释了。我错误地认为计时器与主事件循环分开运行,即使我们卡在插槽中也可以计时。
    • 只是一个愚蠢的问题。为什么在我们单击按钮后,插槽实际上会被执行。内部事件循环不应该阻塞事件调度吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-09
    • 1970-01-01
    • 2011-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多