QRunnable发出信号并从插槽中获取发送者

时间:2017-07-03 17:28:47

标签: c++ qt

QRnnable在完成后被QThreadPool破坏。当我从它发出信号并尝试使用sender()从插槽中获取QRunnable对象时,它为NULL。

最小例子:

// class MyRunnable : public QObject, public QRunnable
MyRunnable::run()
{
    //... do some work
    emit onFinished();
}

// constructor by request
MyRunnable::MyRunnable(QObject *parent) : QObject(parent),
m_someData(1),
{
}

...
private slots:
    void onFinished()
    {
         MyRunnable* myRunnable = qobject_cast<MyRunnable*>(sender());
         int val = myRunnable->getSomething(); // myRunnable is null and it crashes
    }
...

// later I start it using some thread pool
MyRunnable* myRunnable = new MyRunnable;
connect(myRunnable, SIGNAL(onFinished()), this, SLOT(onFinished());
threadPool.start(myRunnable);

有什么方法可以指定何时删除此对象?所以我可以安全地访问我的插槽中的数据成员吗?

2 个答案:

答案 0 :(得分:2)

您想要在对象的成员被销毁后访问它们。显然,你不能安全地做到这一点。

另外,铸造sender()直接访问它是一个危险的标志 - 无论是在不必要的耦合方面还是在线程安全方面。

相反,您可能希望相关成员复制到信号中:

MyRunnable::run()
{
    //... do some work
    emit onFinished(getSomething());
}

并简单地在侦听器中使用结果。

如果你真的相信你必须控制可运行的生命周期,你可以观察到

  

如果runnable->autoDelete()返回true,则线程池将获得runnable的所有权

因此,您可以覆盖autoDelete()以返回false,然后从您的广告位中调用其deleteLater()方法。注意直接访问其成员,因为它仍然是一个线程池线程。

答案 1 :(得分:2)

runnable正在处理请求并生成响应。将这些因素排除在外,问题就解决了:

struct FooRequest;
struct FooResponse;
using FooResponsePtr = std::shared_ptr<FooResponse>;    

class Foo : public QObject, public QRunnable {
  FooRequest m_req;
protected:
  void run() override {
    std::shared_ptr<FooResponse> rsp;
    /* ... */
    emit hasResponse(rsp);
  }
public:
  Foo(const FooRequest & req) : m_req(req) {}
  Foo(FooRequest && req) : m_req(std::move(req)) {}
  Q_SIGNAL void hasResponse(const FooResponsePtr &);
  static void main() {
    qRegisterMetatype<FooResponsePtr>();
  }
};

Q_DECLARE_METATYPE(FooResponsePtr)

int main() {
  Foo::main();
  ...
};

您还可以使用FooResponse使QExplicitlySharedDataPointer显式共享类,使其像其他便宜的Qt值类一样。然后访问将是直接的,而不需要std::shared_ptr。显式共享比隐式共享便宜,如果您不打算保留隐式共享的写时复制行为,则更有意义。