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);
有什么方法可以指定何时删除此对象?所以我可以安全地访问我的插槽中的数据成员吗?
答案 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
。显式共享比隐式共享便宜,如果您不打算保留隐式共享的写时复制行为,则更有意义。