【发布时间】:2020-04-28 17:36:43
【问题描述】:
我正在尝试分析在访问由发送方线程创建并由接收方线程访问的堆分配对象时似乎发生的段错误。
这是代码的简短版本:
#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 在这里不是一个选项,但这样做真的安全吗?
谢谢。
【问题讨论】:
-
对我来说看起来不太安全。另外,不相关的是,您为什么在
QObject::connect(&sender, SIGNAL(datas(Data*)), &receiver, SLOT(onDatas(Data*)));中使用基于旧字符串的SIGNAL和SLOT宏?你真的应该使用新的类型安全变体,它会在编译时失败,而不是在运行时在你搞砸时默默地不做任何事情——它们甚至会产生更快的代码作为奖励。 -
通过作为信号/槽参数传递的指针删除堆分配的对象,您是否看到有关 Qt 的信号/槽机制的任何问题?
-
在显示的代码中,我看不到任何明显的问题。但是...如果
Data不是 POD 或者发送者继续访问与发送的指针相关联的内存,那么显然会有问题。与多线程一样,魔鬼在细节中。另外,如果datas信号连接了多个槽怎么办? -
datas在这里连接到一个唯一的插槽,但如果多个插槽调用delete,则可以肯定此模式将不起作用。 -
通过信号槽连接发送单一所有权指针是灾难的根源。一个信号可能附加了零个或多个监听器。这就是信号的使用方式。如果没有侦听器会泄漏内存,或者在连接两个插槽时导致双重删除的信号 - 只是坏消息。为了传递所有权,您需要使用事件,因为它们保证被销毁一次,即使没有人接受该事件。如果您坚持使用信号槽连接,请使用 ref 计数共享对象或 std::shared_ptr。
标签: c++ linux qt signals-slots