2017-07-03 31 views
0

QRunnable在完成后被QThreadPool销毁。当我从它发出信号并尝试使用sender()从插槽获取QRunnable对象时,它是NULL。QRunnable发出信号并从插槽获取发件人

小例子:

// 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

显示MyRunnable类的构造函数,请 – eyllanesc

回答

2

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

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

相反,你可能想副本相关成员到信号:

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

,并简单地使用在听者的结果。


如果你真的认为你必须控制在可运行的寿命,你可以观察到

线程池需要的可运行的,如果runnable->autoDelete()返回true所有权

所以你可以覆盖autoDelete()以返回false,然后从您的插槽中调用其deleteLater()方法。小心直接访问其成员,因为它仍然是一个线程池线程。

+0

什么样的危险?有没有更清洁的方式来实现这一目标?我有很多想访问的数据成员。在单独的结果对象中发送它们,或者传递如此多的参数看起来不太好。 – lll

+0

如果我知道我做了什么,我可以做到这一点,而不必担心线程安全。解决我的问题的一个解决方案是'setAutoDelete(false)',并在'onFinished()'槽中删除我的QRunnable。 – lll

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

你也可以使用QExplicitlySharedDataPointer,使其像其他廉价到副本Qt的值类使FooResponse一个明确的共享类。访问将是直接的,不需要std::shared_ptr。显式共享比隐式共享更便宜,如果您不打算保留隐式共享的写入时复制行为,则更为合理。