首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >实现QThread的正确方法是什么?(请举例说明...)

实现QThread的正确方法是什么?(请举例说明...)
EN

Stack Overflow用户
提问于 2010-11-04 09:31:53
回答 5查看 85.2K关注 0票数 68

QThread的Qt文档说要从QThread创建一个类,并实现run方法。

下面是摘自4.7Qthread文档...

运行创建您自己的线程,子类QThread并重新实现

()。例如:

 class MyThread : public QThread
 {
 public:
     void run();
 };

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

因此,在我创建的每个线程中,我都做到了这一点,而且在大多数情况下,它都工作得很好(我没有在我的任何对象中实现moveToThread(这),它工作得很好)。

我上周遇到了一个问题(通过绕过我创建对象的位置来设法通过它),并找到了following blog post。这里基本上是说,将QThread子类化确实不是正确的方法(文档也是不正确的)。

这是来自Qt开发人员的,所以乍一看我很感兴趣,经过进一步的思考,同意他的观点。遵循面向对象的原则,你真的只想子类化一个类来进一步增强该类……不仅仅是直接使用类方法……这就是为什么要实例化..。

假设我想要将一个自定义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中进行线程化的一个恰当的例子。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2010-11-04 10:51:17

我能想到的唯一补充就是进一步声明QObject与单个线程具有亲和力。这通常是创建QObject的线程。因此,如果您在应用程序的主线程中创建QObject并希望在另一个线程中使用它,则需要使用moveToThread()来更改亲和性。

这就省去了对QThread进行子类化和在run()方法中创建对象的麻烦,从而使您的东西能够很好地被封装。

这篇博文确实包含了一个example的链接。它非常简短,但它展示了基本的思想。创建QObject,连接信号,创建QThread,将QObjects移动到QThread并启动线程。信号/槽机制将确保正确和安全地跨越线程边界。

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

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

票数 31
EN

Stack Overflow用户

发布于 2013-08-17 06:07:41

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

基本思想是相同的:我创建一个驻留在我的主线程中的QThread实例,一个驻留在我创建的新线程中的worker类实例,然后我连接所有信号。

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

一些背景知识:

ChildProcesses类是一个子进程管理器,它通过调用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()方法,以便在线程就绪时立即启动它。因为监视器位于新线程中,所以watch()方法就是在这个线程中执行的(队列连接用于调用插槽)。

然后,我使用Qt::DirectConnection将ChildProcesses::stop()信号连接到ChildrenWatcher::stop()插槽,因为我需要异步执行此操作。这是必需的,所以当不再需要ChildProcesses管理器时,我的线程会停止。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();
}

哦,isStopped()方法只是在while()条件中使用互斥锁的一种方便方法:

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

所以这里发生的事情是,我在需要完成时设置了stopped标志,然后下一次调用isStopped()时,它返回false,线程结束。

那么当watch()循环结束时会发生什么呢?它调用deleteLater(),因此一旦控制返回到线程事件循环,对象就会自毁,这恰好发生在deleteLater()调用之后(当watch()返回时)。返回到ChildProcesses::start(),您可以看到从监视器的destroyed()信号到线程的quit()槽有一个连接。这意味着当监视器完成时,线程会自动结束。当它完成时,它也会自毁,因为它自己的finished()信号连接到它的deleteLater()插槽。

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

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

票数 10
EN

Stack Overflow用户

发布于 2014-04-27 16:39:15

我不想贬低@sergey-tachenov的优秀答案,但在Qt5中,你可以停止使用信号和槽,简化你的代码,并具有编译时检查的优势:

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();
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4093159

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档