跨不同线程删除槽内的堆分配对象是否安全



我试图分析在访问由发送方线程创建并由接收方线程访问的堆分配对象时出现的segfault。

以下是代码的简短版本:

#include <QCoreApplication>
#include <QDebug>
#include <QThread>
#include <QTimer>
class Data
{
public:
Data(int data1) : m_data1(data1) {}
int data1() {
return m_data1;
}
private:
int m_data1;
};
class Sender : public QObject
{
Q_OBJECT
public:
Sender(int timeout) : m_timeout(timeout) {}
public slots:
void startSendingDatas() {
QTimer::singleShot(m_timeout, [this]() {
emit datas(new Data(3));
});
}
signals:
void datas(Data *data);
private:
int m_timeout;
};
class Receiver : public QObject
{
Q_OBJECT
public slots:
void onDatas(Data *data) {
qDebug() << "data1 = " << data->data1();
delete data; // is it always safe ?
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Sender sender(5000);
Receiver receiver;
QObject::connect(&sender, SIGNAL(datas(Data*)),
&receiver, SLOT(onDatas(Data*)));
QThread worker;
worker.start();
sender.moveToThread(&worker);
// now we call it asynchronously
QMetaObject::invokeMethod(&sender, "startSendingDatas");
return a.exec();
}
#include "main.moc"

Data不是从QObject继承的,所以deleteLater在这里不是一个选项,但这样做真的安全吗?

谢谢。

是的,如果您能够确保指针在访问时仍然有效,那么这样做是"安全的"。

在这个简单的例子中,情况似乎就是这样。我在你的代码中看到了一个潜在的问题,可能是你随机崩溃的原因:

事件驱动的对象只能在单个线程中使用。具体地,这适用于定时器机制和网络模块。例如,不能在非对象线程中启动计时器或连接套接字

来自https://doc.qt.io/qt-5/threads-qobject.htmlparagraph对象可重入性。

这就是您正在做的:sender对象在主线程上创建,因此计时器在该线程上启动,然后移动到工作线程。

还有一件事我不是100%有信心:在将对象移动到另一个线程之前执行连接。默认情况下,如果对连接只字未提,那么当两个对象在同一个线程上时,它是一个直接连接,而当对象在不同的线程中时,它显然是一个排队连接。我不知道当一个对象被移动时,Qt在哪种扩展上对改变连接类型是稳健的,但我宁愿先移动对象,然后连接它。

最新更新