我有一个用C ++编写的应用程序& Qt做了很多网络请求。我的代码的基本概要如下:
{
QNetworkReply* reply = networkAccessManager().get( QNetworkRequest( url ) );
assert( reply );
connect( reply, &QNetworkReply::finished, [=]
{
// do action based on the contents of the reply
assert( reply->isFinished() );
reply->deleteLater();
});
}
代码同时保留多个飞行请求。这两个断言从未被解雇过。
随机(大约每200000个请求),此回复的延迟删除失败,看起来是双重免费的。在Qt 5.0.2和Qt 5.2.x中都会发生这种情况。我已经运行了valgrind,结果如下:
==18792== Invalid read of size 8
==18792== at 0x53AAC7A: QObject::~QObject() (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.0.2)
==18792== by 0x4EB60A8: ??? (in /usr/lib/x86_64-linux-gnu/libQt5Network.so.5.0.2)
==18792== by 0x53A4357: QObject::event(QEvent*) (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.0.2)
==18792== by 0x537EBBC: QCoreApplication::notify(QObject*, QEvent*) (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.0.2)
==18792== by 0x537E8BD: QCoreApplication::notifyInternal(QObject*, QEvent*) (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.0.2)
==18792== by 0x5380AC5: QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.0.2)
==18792== by 0x53C38D4: QEventDispatcherUNIX::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.0.2)
==18792== by 0x537D88A: QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.0.2)
==18792== by 0x51F422A: QThread::exec() (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.0.2)
==18792== by 0x51F8A4A: ??? (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.0.2)
==18792== by 0x5812B4F: start_thread (pthread_create.c:304)
==18792== by 0x62A1A7C: clone (clone.S:112)
==18792== Address 0xb9fd670 is 0 bytes inside a block of size 16 free'd
==18792== at 0x4C279DC: operator delete(void*) (vg_replace_malloc.c:457)
==18792== by 0x53A4357: QObject::event(QEvent*) (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.0.2)
==18792== by 0x537EBBC: QCoreApplication::notify(QObject*, QEvent*) (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.0.2)
==18792== by 0x537E8BD: QCoreApplication::notifyInternal(QObject*, QEvent*) (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.0.2)
==18792== by 0x5380AC5: QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.0.2)
==18792== by 0x53C38D4: QEventDispatcherUNIX::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.0.2)
==18792== by 0x537D88A: QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.0.2)
==18792== by 0x538115F: QCoreApplication::exec() (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.0.2)
==18792== by 0x4093C4: main (main.cpp:38)
我认为以下事情确实如此:
我认为,但无法验证,以下情况属实:
我在尝试解决此错误时遇到了困难。从堆栈跟踪的检查中,看起来reply->deleteLater()
信号以某种方式传递给网络线程和主线程。但我不明白这是怎么回事。信号和插槽编程风格使得很难确定出现错误的地方。
我将如何调试此错误?
答案提到了同步的可能起源。在我的代码库中,只允许从不同的线程调用1个类。这个类的功能分为两类:
第二类实现为:
class Foo {
Q_OBJECT
public:
void Foo() {
connect( foo, &Foo::doSomethingSignal, this, &Foo:doSomethingInternal, Qt::QueuedConnection );
}
// this functions gets called from various threads
void doSomething() {
emit( doSomethingSignal() );
}
private slots:
// this function happens synchronized in the main thread
void doSomethingInternal() {
...
}
signals:
void doSomethingSignal();
}
根据此stackoverflow问题:emit Qt signal from non Qt Thread or ouside Qt main event loop with at 4.5这是安全的。调用者不是QObject。
答案 0 :(得分:1)
回答我自己的问题:
我已经制作了一个具有相当最小功能的测试用例。经过一些测试后,我的测试用例中似乎存在错误。这就是我制作错误报告的原因:https://bugreports.qt-project.org/browse/QTBUG-38309
可在此处查看测试用例:https://bitbucket.org/sdessens/qnetworkreply-access-violation-testcase/overview
存在一种解决方法,包括在删除回复之前等待几百毫秒,这在测试用例中工作正常,但由于某种原因不在我的应用程序中(几分钟后,事件循环似乎停止工作)。随机崩溃的影响不是地球散射,所以现在我在bash中坚持一个while循环以保持我的应用程序运行,直到Qt开发人员解决这个问题。
答案 1 :(得分:0)
从你的Valgrind错误报告中,看起来我们试图在其他一些线程释放它之后尝试读取内存。它看起来不像双重免费场景,而不是免费使用后。
== 18792 ==读取大小为8
== 18792 ==地址0xb9fd670是一个大小为16的块内的0字节免费
您可以查看我之前在Valgrind上的post以及如何在程序报告第一次错误时一起使用GDB / Valgrind来执行实时调试。
这个问题似乎是由于线程之间的同步问题,并且有时候一个线程正在释放其他线程不知道的线程。多线程环境中的内存相关问题很难理解和解决。您可能想要考虑使用上述注释中提供的建议(基于C ++ / RAII的类中的智能指针)。