暂停 QThread

发布于 2024-11-04 13:14:35 字数 469 浏览 1 评论 0原文

UpdateThread is-a QThreadUpdateThread::run() 中设置一个调用 slot QTimer >UpdateThread::tick() 每 t 毫秒。现在,根据某些条件,我需要暂停线程,一段时间后,根据另一个条件,我需要唤醒它。


  • 我做 QTimer 事情的方式可以吗?或者我应该将 tick 代码移动到 run 并每隔 t ms 调用 QThread::start()
  • 如何有条件地暂停和唤醒线程
  • 或者我应该只是 stop() QTimer 和 start() 后者?

UpdateThread is-a QThread That sets up a QTimer in UpdateThread::run() that calls slot UpdateThread::tick() every t ms. Now based upon some condition I need to Pause the Thread and after some time based upon another condition I need to wake it up.


  • Is the Way I am doing the QTimer thing is Okay ? or I should move the tick code to run and call the QThread::start() every t ms ?
  • How can I Pause and wake up the threads conditionally
  • Or I should just stop() the QTimer and start() it latter ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

ぃ双果 2024-11-11 13:14:35

首先,您不应该在 QThread 子类上定义插槽并从 run() 中调用它们 - 插槽将被执行(通过执行跨线程插槽调用)在拥有 UpdateThread 实例的线程上下文中(与创建该实例的线程相同,除非您对其调用 moveToThread() ),而不是在 UpdateThread 表示的线程上下文中。记住这个助记符:

run()中,QThread::thread()!= this

相反,在您在 run() 中创建的 QObject 子类上定义插槽>。

好吧,抛开这个问题,让我们看一下计时器。 QTimer 文档包含以下内容:

在多线程应用程序中,您可以在任何具有事件循环的线程中使用QTimer
要从非 GUI 线程启动事件循环,请使用 QThread::exec( )。 Qt 使用定时器
线程亲和性来确定哪个线程将发出 timeout() 信号。
因此,您必须在其线程中启动和停止计时器;这是不可能的
从另一个线程启动计时器。

(强调我的)特别注意最后一句。

解决方案是跨线程调用 QTimer::start()QTimer::stop()。您可能了解跨线程信号/槽连接。这使用相同的底层机制,该机制在 QMetaObject::invokeMethod( )

class UpdateThread : public QThread {
    Q_OBJECT
private:
    QObject * m_timer; // just a QObject* so we're not tempted
                       // to call functions on it
    QMutext m_mutex; // protects 'm_timer'
public:
    explicit UpdateThread( QObject * parent=0 )
        : QThread( parent ), m_timer( 0 ) {}
    // ...
private:
    /* reimpl */ void run() {
        QTimer timer;
        // ...'timer' setup code goes here...
        {
            const QMutexLocker locker( &m_mutex );
            m_timer = &timer; // publish 'timer' through 'm_timer'
        }
        // main code of run()
        exec(); // start event loop so we get timer's timeout()s
                // and we can receive cross-thread method calls
        {
            const QMutexLocker locker( &m_mutex );
            m_timer = 0; // un-publish before we delete `timer`
        }
    }
public Q_SLOTS:
    void startTimer() {
        const QMutexLocker locker( &m_mutex );
        if ( !m_timer ) return;
        // perform cross-thread method call:
        QMetaObject::invokeMethod( m_timer, "start", Qt::QueuedConnection );
    }
    void stopTimer() {
        const QMutexLocker locker( &m_mutex );
        if ( !m_timer ) return;
        // perform cross-thread method call:
        QMetaObject::invokeMethod( m_timer, "stop", Qt::QueuedConnection );
    }
};

现在,这就是从 GUI 线程启动/停止计时器的方法。但您也询问了替代方案。

  1. tick() 代码移至 run() 中,每隔 t 调用 UpdateThread::start()毫秒。

    这是次优的,因为它会每隔 t 毫秒创建和销毁线程。线程创建仍然是一个昂贵的操作。另外,如果在您下次调用 start()UpdateThread::run() 尚未完成,您将丢失计时器滴答声。

  2. UpdateThread 如上所述。

    这还不错,但我想说,这不是惯用的多线程。如果计时器触发如此频繁,以至于仅会以某种方式减慢 GUI 线程的速度,那么这是一个很好的解决方案,尽管您也可能会以这种方式丢失计时器滴答声。

  3. QThreadPool

    我最喜欢的。将执行 tick() 的代码移至 QRunnable::run() 的实现中,并在计时器触发时在线程池中对新的可运行对象进行排队。在这种情况下,计时器最自然地存在于 GUI 线程中,从而避免了如上所述的跨线程方法调用的需要。除非 GUI 线程本身超载,否则您不会错过任何计时器滴答声。您还可以免费缩放系统中的核心数量(如果您不希望这样做,请不要使用 QThreadPool::globalInstance() ,而是创建您自己的实例并调用 setMaxThreadCount(1)).

First of all, you shouldn't define slots on your QThread subclass and call them from within run() - the slots will be executed (by performing a cross-thread slot invocation) in the context of the thread that owns your UpdateThread instance (the same one that created it, unless you called moveToThread() on it), not in the context of the thread represented by UpdateThread. Remember this mnemonic:

In run(), QThread::thread() != this

Instead, define the slots on a QObject subclass that you create inside run().

Ok, with that out of the way, let's have a look at the timer. The QTimer documentation contains the following:

In multithreaded applications, you can use QTimer in any thread that has an event loop.
To start an event loop from a non-GUI thread, use QThread::exec(). Qt uses the timer's
thread affinity to determine which thread will emit the timeout() signal.
Because of this, you must start and stop the timer in its thread; it is not possible to
start a timer from another thread.

(emphasis mine) Take particular note of the last sentence.

The solution is to do a cross-thread call of QTimer::start() and QTimer::stop(). You might know about cross-thread signal/slot connections. This uses the same underlying mechanism, which is exposed in QMetaObject::invokeMethod():

class UpdateThread : public QThread {
    Q_OBJECT
private:
    QObject * m_timer; // just a QObject* so we're not tempted
                       // to call functions on it
    QMutext m_mutex; // protects 'm_timer'
public:
    explicit UpdateThread( QObject * parent=0 )
        : QThread( parent ), m_timer( 0 ) {}
    // ...
private:
    /* reimpl */ void run() {
        QTimer timer;
        // ...'timer' setup code goes here...
        {
            const QMutexLocker locker( &m_mutex );
            m_timer = &timer; // publish 'timer' through 'm_timer'
        }
        // main code of run()
        exec(); // start event loop so we get timer's timeout()s
                // and we can receive cross-thread method calls
        {
            const QMutexLocker locker( &m_mutex );
            m_timer = 0; // un-publish before we delete `timer`
        }
    }
public Q_SLOTS:
    void startTimer() {
        const QMutexLocker locker( &m_mutex );
        if ( !m_timer ) return;
        // perform cross-thread method call:
        QMetaObject::invokeMethod( m_timer, "start", Qt::QueuedConnection );
    }
    void stopTimer() {
        const QMutexLocker locker( &m_mutex );
        if ( !m_timer ) return;
        // perform cross-thread method call:
        QMetaObject::invokeMethod( m_timer, "stop", Qt::QueuedConnection );
    }
};

Now, this is how you start/stop the timer from the GUI thread. But you were also asking about alternatives.

  1. Move tick() code into run(), call UpdateThread::start() every t milliseconds.

    This is suboptimal, as it would create and destroy threads every t ms. Thread creation is still an expensive operation. Also, if UpdateThread::run() isn't done by the time you next call start(), you'll lose timer ticks.

  2. UpdateThread as outlined above.

    This isn't too bad, but it's not idiomatic multithreading, I'd say. It's a good solution if the timer fires so often that that alone would slow down the GUI thread somehow, though you might lose timer ticks this way, too.

  3. QThreadPool

    My favourite. Move the code that performs tick() into an implementation of QRunnable::run(), and queue a new runnable on a thread pool whenever the timer fires. In this case, the timer would most naturally live in the GUI thread, avoiding the need for cross-thead method calls as outlined above. Unless the GUI thread itself is overloaded, you won't miss any timer ticks. You also get for free scaling to the number of cores in the system (if you don't want that, don't use QThreadPool::globalInstance() but create your own instance and call setMaxThreadCount(1)).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文