QThread 对象作为工作类的成员

Posted

技术标签:

【中文标题】QThread 对象作为工作类的成员【英文标题】:QThread object as a member of worker class 【发布时间】:2016-04-04 13:24:14 【问题描述】:

我已经阅读了很多关于为什么在大多数情况下子类化QThread 是一个坏主意以及如何正确使用QThread 的文章,调用moveToThread 方法。 Here我们可以看到这种设计的典型例子。

我正在设计的班级应该满足以下要求:

它想使用信号和槽,所以我需要一个事件循环并使用moveToThread

它将仅公开带有信号和插槽的接口。没有普通的 C++ 方法。

所有槽都应该在对象的专用线程中执行,每个对象一个线程。所以线程应该在创建对象时创建,并且应该在对象死亡时结束。

因此想到了一个明显的解决方案(未经测试,只是一个草图代码):

class Worker : public QObject 
Q_OBJECT

public:
    Worker() 
        thread = new QThread();
        // ...Some signal-slot connections may be done here...
        // ...Some other connections may be performed by user code...
        moveToThread(thread);
        thread->start();
    

    ~Worker() 
        thread->exit();
        thread->wait();
        delete thread;
    

public slots:
    void process(); // and other interface slots

signals:
    // Interface signals

private:
    QThread* thread;
;

所以重点是将QThread 对象声明为工人类的(私有)成员,但我从未在任何示例或其他人的代码中看到过。

这就是为什么我想知道这个设计是否有缺陷?它有一些我没有注意到的致命缺点吗?或者没关系,只是不经常需要?

【问题讨论】:

首先,使用某种 RAII 对象,例如(unique_ptr 等)。其次,这可能更适合 CodeReview SE 网站。 @OMGtechy 这可能会在 Code Review 上关闭,作者提到 “未测试,只是一个草图代码”。代码审查需要真实、有效的代码。看看那里是什么on-topic 以供参考。 @Phrancis 啊,在这种情况下,我建议先试试@Sergey! 未测试 - 如果您自己尝试一些事情,然后根据您的研究提出您不理解的问题,您将学到更多。 @OMGtechy 我看到这个问题在这个网站上有点离题。也许应该把它移到programmers.stackexchange.com? 【参考方案1】:

只要将对象移出工作线程,这是可能的。以下是您可以这样做的方法 - 请注意,您应该按值保存线程,没有意义不使用编译器为您管理内存。

class Worker : public QObject 
  Q_OBJECT
  QThread m_thread;
public:
  Worker() 
    m_thread.start();
    moveToThread(&m_thread);
  
  ~Worker() 
    // Move us out of any thread.
    // moveToThread must always be called from QObject::thread()!
    
      QObject sig;
      sig.connect(&sig, &QObject::destroyed, this, [this]
        this->moveToThread(0); // become thread-less
        m_thread->quit();
      );
    
    // Wait for the thread to stop
    m_thread.wait();
  
;

鉴于可以通过QtConcurrent::run 异步完成工作,您很可能无论如何都不应该使用这样的对象。最有可能的是,您将浪费大部分空闲的线程,因为您不太可能使线程始终可运行。不可运行的线程本质上是一种资源浪费。

【讨论】:

我想这段代码什么都不做:QThread * curThread = thread(); this->moveToThread(curThread);。另外我认为没有必要在析构函数中使用线程更改技巧。哪个线程会销毁对象并不重要。 @hank 我认为代码是必要的,因为不能保证调用析构函数的线程是由 m_thread 处理的。官方文档 (doc.qt.io/qt-5/threads-qobject.html) 说“从拥有对象(或以其他方式访问对象)的线程以外的线程调用 QObject 上的删除是不安全的......”。 @Sergey “……不安全,除非你保证对象此时没有处理事件”。退出并停止线程后,对象不会处理任何事件。 @hank 该对象不处理任何事件,但object->thread() 非零且不同于QThread::currentThread()。这表明不应访问对象的非线程安全方法。对于一个足够复杂的对象,上面介绍的方法确保该对象不会在其他线程中运行,无论出于何种原因。这可能是矫枉过正,但它是廉价的保证。

以上是关于QThread 对象作为工作类的成员的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能在类的成员函数中初始化 QThread?

如何使用cpp中的QThread使用一个对象运行同一类的两个线程?

将对象的亲和性从 QThread 更改为主 GUI 线程

如何向 Qt 中 QThread 类的特定对象发出信号?

为啥需要 processEvents() 才能让 QThread 工作?

Qt线程间的信号与槽 以及 QThread