代码之家  ›  专栏  ›  技术社区  ›  g19fanatic

实现QThread的正确方法是什么…(请举例…)

  •  60
  • g19fanatic  · 技术社区  · 14 年前

    以下摘自4.7 Qthread文档。。。

    若要创建自己的线程,请子类QThread和reimplement run()。例如:

     class MyThread : public QThread
     {
     public:
         void run();
     };
    
     void MyThread::run()
     {
         QTcpSocket socket;
         // connect QTcpSocket's signals somewhere meaningful
         ...
         socket.connectToHost(hostName, portNumber);
         exec();
     }
    

    上周我遇到了一个障碍(通过在创建对象的地方工作,我成功地克服了这个障碍),发现 following blog post . 这里基本上说,子类化QThread并不是正确的方法(文档也不正确)。

    假设我想将自定义QObject类移动到线程。。。做这件事的正确方法是什么?在那篇博文中,他“说”他在某个地方有个例子。。。但如果有人能进一步向我解释,我将不胜感激!

    由于这个问题备受关注,下面是4.8文档的一个拷贝和粘贴,其中包含了实现QThread的“正确”方法。

    class Worker : public QObject
     {
         Q_OBJECT
         QThread workerThread;
    
     public slots:
         void doWork(const QString &parameter) {
             // ...
             emit resultReady(result);
         }
    
     signals:
         void resultReady(const QString &result);
     };
    
     class Controller : public QObject
     {
         Q_OBJECT
         QThread workerThread;
     public:
         Controller() {
             Worker *worker = new Worker;
             worker->moveToThread(&workerThread);
             connect(workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
             connect(this, SIGNAL(operate(QString)), worker, SLOT(doWork(QString)));
             connect(worker, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString)));
             workerThread.start();
         }
         ~Controller() {
             workerThread.quit();
             workerThread.wait();
         }
     public slots:
         void handleResults(const QString &);
     signals:
         void operate(const QString &);
     };
    

    Worker::workerThread 不必要且从未在其示例中使用的成员。去掉那一块,这就是如何在Qt中进行线程处理的一个恰当的例子。

    4 回复  |  直到 7 年前
        1
  •  31
  •   leyyin mtvec    9 年前

    我唯一想补充的是 QObject Q对象 . 所以如果你创造了一个 在应用程序的主线程中,如果要在其他线程中使用它,则需要使用 moveToThread() 改变亲和力。

    这样就省去了子类 QThread run() 方法,从而使您的内容得到很好的封装。

    那篇博文包含了一个指向 example Q对象 Q螺纹 ,移动 QObjects Q螺纹 启动线程。信号/插槽机制将确保正确和安全地跨越线程边界。

    如果必须在该机制之外调用对象上的方法,则可能必须引入同步。

    我知道Qt还有其他好东西 threading facilities 除了可能值得熟悉的线程之外,我还没有这样做:)

        2
  •  9
  •   Sergei Tachenov    12 年前

    这是 one example of how to use QThread correctly ,但它也有一些问题,这些问题反映在评论中。特别是,由于插槽的执行顺序没有严格定义,因此可能会导致各种问题。2013年8月6日发表的评论给出了一个如何处理这个问题的好主意。我在程序中使用了类似的方法,下面是一些示例代码。

    void ChildProcesses::start()
    {
        QThread *childrenWatcherThread = new QThread();
        ChildrenWatcher *childrenWatcher = new ChildrenWatcher();
        childrenWatcher->moveToThread(childrenWatcherThread);
        // These three signals carry the "outcome" of the worker job.
        connect(childrenWatcher, SIGNAL(exited(int, int)),
                SLOT(onChildExited(int, int)));
        connect(childrenWatcher, SIGNAL(signalled(int, int)),
                SLOT(onChildSignalled(int, int)));
        connect(childrenWatcher, SIGNAL(stateChanged(int)),
                SLOT(onChildStateChanged(int)));
        // Make the watcher watch when the thread starts:
        connect(childrenWatcherThread, SIGNAL(started()),
                childrenWatcher, SLOT(watch()));
        // Make the watcher set its 'stop' flag when we're done.
        // This is performed while the watch() method is still running,
        // so we need to execute it concurrently from this thread,
        // hence the Qt::DirectConnection. The stop() method is thread-safe
        // (uses a mutex to set the flag).
        connect(this, SIGNAL(stopped()),
                childrenWatcher, SLOT(stop()), Qt::DirectConnection);
        // Make the thread quit when the watcher self-destructs:
        connect(childrenWatcher, SIGNAL(destroyed()),
                childrenWatcherThread, SLOT(quit()));
        // Make the thread self-destruct when it finishes,
        // or rather, make the main thread delete it:
        connect(childrenWatcherThread, SIGNAL(finished()),
                childrenWatcherThread, SLOT(deleteLater()));
        childrenWatcherThread->start();
    }
    

    一些背景:

    childprocesss类是一个子进程管理器,它使用spawn()调用启动新的子进程,保持当前运行的进程列表,等等。但是,它需要跟踪子状态,这意味着在Linux上使用waitpid()调用或在Windows上使用WaitForMultipleObjects。我以前用定时器在非阻塞模式下调用它们,但现在我需要更迅速的反应,这意味着阻塞模式。这就是线索的来源。

    ChildrenWatcher类定义如下:

    class ChildrenWatcher: public QObject {
        Q_OBJECT
    private:
        QMutex mutex;
        bool stopped;
        bool isStopped();
    public:
        ChildrenWatcher();
    public slots:
        /// This is the method which runs in the thread.
        void watch();
        /// Sets the stop flag.
        void stop();
    signals:
        /// A child process exited normally.
        void exited(int ospid, int code);
        /// A child process crashed (Unix only).
        void signalled(int ospid, int signal);
        /// Something happened to a child (Unix only).
        void stateChanged(int ospid);
    };
    

    这就是它的工作原理。当所有这些东西都启动时,调用ChildProcess::start()方法(见上文)。它创建一个新的QThread和一个新的ChildrenWatcher,然后将其移动到新线程。然后,我连接三个信号,通知我的经理关于其子进程的命运(退出/信号/上帝知道发生了什么)。然后开始主要的乐趣。

    我将QThread::started()连接到ChildrenWatcher::watch()方法,以便在线程准备好后立即启动它。由于watcher位于新线程中,因此watch()方法在该线程中执行(队列连接用于调用插槽)。

    然后,我使用Qt::DirectConnection将ChildProcesses::stopped()信号连接到ChildrenWatcher::stop()插槽,因为我需要异步连接。这是必需的,因此当不再需要childprocesss管理器时,我的线程将停止。stop()方法如下:

    void ChildrenWatcher::stop()
    {
        mutex.lock();
        stopped = true;
        mutex.unlock();
    }
    

    然后ChildrenWatcher::watch():

    void ChildrenWatcher::watch()
    {
      while (!isStopped()) {
        // Blocking waitpid() call here.
        // Maybe emit one of the three informational signals here too.
      }
      // Self-destruct now!
      deleteLater();
    }
    

    噢,issTop()方法只是在while()条件下使用互斥量的一种方便方法:

    bool ChildrenWatcher::isStopped()
    {
        bool stopped;
        mutex.lock();
        stopped = this->stopped;
        mutex.unlock();
        return stopped;
    }
    

    那么watch()循环结束时会发生什么?它调用deleteLater(),这样当控件返回到线程事件循环时,对象就会自毁,而线程事件循环正好发生在deleteLater()调用之后(watch()返回时)。返回ChildProcesses::start(),您可以看到从监视程序的destroyed()信号到线程的quit()插槽之间存在连接。这意味着线程将在观察程序完成时自动完成。当它完成时,它也会自毁,因为它自己的finished()信号连接到它的deleteLater()插槽。

    这与Maya发布的想法几乎相同,但因为我使用自毁习惯用法,所以不需要依赖于调用插槽的顺序。它总是先自毁,稍后停止线程,然后也自毁。我可以在工作进程中定义finished()信号,然后将其连接到自己的deleteLater(),但这只意味着多了一个连接。由于我不需要finished()信号用于任何其他目的,所以我选择只从工作进程本身调用deleteLater()。

    Maya还提到不应该在worker的构造函数中分配新的qobject,因为它们不会存在于将worker移动到的线程中。我说无论如何都要做,因为OOP就是这样工作的。只需确保所有这些QObject都是worker的子对象(即,使用QObject(QObject*)构造函数)-moveToThread()将所有子对象与要移动的对象一起移动。如果您真的需要QObjects不是对象的子对象,那么在工作线程中重写moveToThread(),以便它也移动所有必要的内容。

        3
  •  3
  •   parsley72    11 年前

    void ChildProcesses::start()
    {
        QThread *childrenWatcherThread = new QThread();
        ChildrenWatcher *childrenWatcher = new ChildrenWatcher();
        childrenWatcher->moveToThread(childrenWatcherThread);
        // These three signals carry the "outcome" of the worker job.
        connect(childrenWatcher, ChildrenWatcher::exited,
                ChildProcesses::onChildExited);
        connect(childrenWatcher, ChildrenWatcher::signalled,
                ChildProcesses::onChildSignalled);
        connect(childrenWatcher, ChildrenWatcher::stateChanged,
                ChildProcesses::onChildStateChanged);
        // Make the watcher watch when the thread starts:
        connect(childrenWatcherThread, QThread::started,
                childrenWatcher, ChildrenWatcher::watch);
        // Make the watcher set its 'stop' flag when we're done.
        // This is performed while the watch() method is still running,
        // so we need to execute it concurrently from this thread,
        // hence the Qt::DirectConnection. The stop() method is thread-safe
        // (uses a mutex to set the flag).
        connect(this, ChildProcesses::stopped,
                childrenWatcher, ChildrenWatcher::stop, Qt::DirectConnection);
        // Make the thread quit when the watcher self-destructs:
        connect(childrenWatcher, ChildrenWatcher::destroyed,
                childrenWatcherThread, QThread::quit);
        // Make the thread self-destruct when it finishes,
        // or rather, make the main thread delete it:
        connect(childrenWatcherThread, QThread::finished,
                childrenWatcherThread, QThread::deleteLater);
        childrenWatcherThread->start();
    }
    
        4
  •  2
  •   Zaid    7 年前

    子类化qthread类仍将在原始线程中运行代码。我想在已经使用GUI线程(主线程)的应用程序中运行一个udp侦听器,当udp侦听器工作正常时,GUI被子类qthread事件处理程序阻塞而冻结。我认为g19发烧友发布的内容是正确的,但是您还需要工作线程来成功地将对象迁移到新线程。我发现 this 文章详细描述了QT中线程的Do和don's。

    在决定子类QThread之前必须阅读!

        5
  •  0
  •   IMAN4K    5 年前

    Qt5 就这么简单: worker.h :

    /*
    * This is a simple, safe and best-practice way to demonstrate how to use threads in Qt5.
    * Copyright (C) 2019 Iman Ahmadvand
    *
    * This is free software: you can redistribute it and/or modify
    * it under the terms of the GNU General Public License as published by
    * the Free Software Foundation, either version 3 of the License, or
    * (at your option) any later version.
    *
    * It is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    * GNU General Public License for more details.
    */
    
    #ifndef _WORKER_H
    #define _WORKER_H
    
    #include <QtCore/qobject.h>
    #include <QtCore/qbytearray.h>
    #include <QtCore/qthread.h>
    #include <QtGui/qevent.h>
    
    namespace concurrent {
    
        class EventPrivate;
        class Event : public QEvent {
        public:
            enum {
                EventType1 = User + 1
            };
    
            explicit Event(QEvent::Type);
            Event(QEvent::Type, const QByteArray&);
    
            void setData(const QByteArray&);
            QByteArray data() const;
    
        protected:
            EventPrivate* d;
        };
    
        class WorkerPrivate;
        /* A worker class to manage one-call and permanent tasks using QThread object */
        class Worker : public QObject {
            Q_OBJECT
    
        public:
            Worker(QThread*);
            ~Worker();
    
        protected slots:
            virtual void init();
    
        protected:
            bool event(QEvent*) override;
    
        protected:
            WorkerPrivate* d;
    
        signals:
            /* this signals is used for one call type worker */
            void finished(bool success);
        };
    
    } // namespace concurrent
    
    #endif // !_WORKER_H
    

    worker.cpp

    /*
    * This is a simple, safe and best-practice way to demonstrate how to use threads in Qt5.
    * Copyright (C) 2019 Iman Ahmadvand
    *
    * This is free software: you can redistribute it and/or modify
    * it under the terms of the GNU General Public License as published by
    * the Free Software Foundation, either version 3 of the License, or
    * (at your option) any later version.
    *
    * It is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    * GNU General Public License for more details.
    */
    
    #include "worker.h"
    
    using namespace concurrent;
    
    class concurrent::EventPrivate {
    public:
        QByteArray data;
    };
    
    Event::Event(QEvent::Type t):QEvent(t), d(new EventPrivate) {
        setAccepted(false);
    }
    
    Event::Event(QEvent::Type t, const QByteArray& __data) : Event(t) {
        setData(__data);
    }
    
    void Event::setData(const QByteArray& __data) {
        d->data = __data;
    }
    
    QByteArray Event::data() const {
        return d->data;
    }
    
    
    
    class concurrent::WorkerPrivate {
    public:
        WorkerPrivate() {
    
        }
    };
    
    Worker::Worker(QThread* __thread) :QObject(nullptr), d(new WorkerPrivate) {
        moveToThread(__thread);
    
        QObject::connect(__thread, &QThread::started, this, &Worker::init);
        QObject::connect(this, &Worker::finished, __thread, &QThread::quit);
        QObject::connect(__thread, &QThread::finished, __thread, &QThread::deleteLater);
        QObject::connect(__thread, &QThread::finished, this, &Worker::deleteLater);
    }
    
    Worker::~Worker() {
        /* do clean up if needed */
    }
    
    void Worker::init() {
        /* this will called once for construction and initializing purpose */
    }
    
    bool Worker::event(QEvent* e) {
        /* event handler */
        if (e->type() == Event::EventType1) {
            /* do some work with event's data and emit signals if needed */
            auto ev = static_cast<Event*>(e);
            ev->accept();
        }
        return QObject::event(e);
    }
    

    usage.cpp :

    #include <QtCore/qcoreapplication.h>
    #include "worker.h"
    
    using namespace concurrent;
    
    Worker* create(bool start) {
        auto worker = new Worker(new QThread);
        if (start)
            worker->thread()->start();
    
        return worker;
    }
    
    int main(int argc, char *argv[]) {
        QCoreApplication app(argc, argv);
        auto worker = create(true);
        if (worker->thread()->isRunning()) {
            auto ev = new Event(static_cast<QEvent::Type>(Event::EventType1));
            qApp->postEvent(worker, ev, Qt::HighEventPriority);
        }
        return app.exec();
    }