Qt多线程应用程序冻结,多个线程等待同一个互斥体

时间:2019-03-01 01:20:40

标签: c linux multithreading qt glibc

我在基于Qt的多线程应用程序中遇到一个奇怪的问题。运行几天后,该应用程序将冻结,没有任何响应。

冻结发生后,我可以确认几个线程(包括主线程)处于futex_wait_queue_me状态。当我附加到该应用程序以通过GDB调查线程状态时,这些线程的回溯  显示它们都停止在具有相同参数futex=0x45a2f8b8 <main_arena>的以下函数中。

__lll_lock_wait_private (futex=0x45a2f8b8 <main_arena>)

我知道在Linux上,在信号处理程序中使用非异步安全功能是此状态的可能原因之一,即,多个线程在等待相同的互斥体,(我可以从回溯中确认它们都在malloc处停止了( )/ free()相关的函数调用),但是在确认我的Qt应用程序之后,我找不到与Linux信号处理程序相关的实现。 (但我不确定Qt核心库是否在其信号/插槽机制中使用Linux信号处理程序。)

很抱歉,我无法提供此问题的源代码,因为这是一个庞大的项目。您想告诉我这种现象的一些可能原因,还是一些有关如何调试它的建议?

谢谢。

更新1:

我可以提供回溯,但抱歉,我必须删除一些明智的信息。

子线程的回溯:

#0 in __lll_lock_wait_private (futex=0x4ad078b8 <main_arena>)
#1 in __GI___libc_malloc (bytes=32) at malloc.c:2918
... ...
#11 in SystemEventImp::event(QEvent*) () 
#12 in QApplicationPrivate::notify_helper(QObject*, QEvent*) ()
#13 in QApplication::notify(QObject*, QEvent*) ()
#14 in QCoreApplication::notifyInternal(QObject*, QEvent*) ()
#15 in QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) ()
#16 in QCoreApplication::sendPostedEvents (receiver=0x0, event_type=0) at kernel/qcoreapplication.cpp:1329
#17 in QWindowSystemInterface::sendWindowSystemEvents (flags=...) at kernel/qwindowsysteminterface.cpp:560
#18 in QUnixEventDispatcherQPA::processEvents (this=0x8079958, flags=...) at eventdispatchers/qunixeventdispatcher.cpp:70
#19 in QEventLoop::processEvents (this=0xbfffef50, flags=...) at kernel/qeventloop.cpp:136
#20 in QEventLoop::exec (this=0xbfffef50, flags=...) at kernel/qeventloop.cpp:212
#21 in QCoreApplication::exec () at kernel/qcoreapplication.cpp:1120
#22 in QGuiApplication::exec () at kernel/qguiapplication.cpp:1220
#23 in QApplication::exec () at kernel/qapplication.cpp:2689
#24 in main(argc=2, argv=0xbffff294)

主线程的回溯:

#0 in __lll_lock_wait_private (futex=0x4ad078b8 <main_arena>) at ../ports/sysdeps/unix/sysv/linux/arm/nptl/lowlevellock.c:32
#1 in __GI___libc_malloc (bytes=8) at malloc.c:2918
... ...
#15 in QGraphicsView::paintEvent(QPaintEvent*) ()
#16 in QWidget::event(QEvent*) () 
#17 in QFrame::event(QEvent*) () 
#18 in QGraphicsView::viewportEvent(QEvent*) ()
#19 in Platform::Drawing::GraphicsView::viewportEvent(QEvent*) ()
#20 in QAbstractScrollAreaFilter::eventFilter(QObject*, QEvent*) ()
#21 in QCoreApplicationPrivate::cancel_handler(QObject*, QEvent*) ()
#22 in QApplicationPrivate::notify_helper(QObject*, QEvent*) ()
#23 in QApplication::notify(QObject*, QEvent*) ()
#24 in QCoreApplication::notifyInternal(QObject*, QEvent*) ()
#25 in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) [clone .part.175] () 
#26 in QWidgetBackingStore::sync() ()
#27 in QWidgetPrivate::syncBackingStore() ()
#28 in QWidget::event(QEvent*) ()
#29 in QApplicationPrivate::notify_helper(QObject*, QEvent*) ()
#30 in QApplication::notify(QObject*, QEvent*) ()
#31 in QCoreApplication::notifyInternal(QObject*, QEvent*) ()
#32 in QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) ()
#33 in QCoreApplication::sendPostedEvents (receiver=0x809ea50, event_type=77)
#34 in QGraphicsViewPrivate::dispatchPendingUpdateRequests (this=0x80e4418)
#35 in QGraphicsScenePrivate::_q_processDirtyItems (this=0x80de238) at graphicsview/qgraphicsscene.cpp:508
#36 in QGraphicsScene::qt_static_metacall (_o=0x80d1a80, _c=QMetaObject::InvokeMetaMethod, _id=15, _a=0x865e238)
#37 in QMetaCallEvent::placeMetaCall (this=0x898d020, object=0x80d1a80)
#38 in QObject::event (this=0x80d1a80, e=0x898d020) at kernel/qobject.cpp:1070
#39 in QGraphicsScene::event (this=0x80d1a80, event=0x898d020) at graphicsview/qgraphicsscene.cpp:3478
#40 in QApplicationPrivate::notify_helper (this=0x8077ba0, receiver=0x80d1a80, e=0x898d020) at kernel/qapplication.cpp:3457
#41 in QApplication::notify (this=0x8077970, receiver=0x80d1a80, e=0x898d020) at kernel/qapplication.cpp:2878
#42 in QCoreApplication::notifyInternal (this=0x8077970, receiver=0x80d1a80, event=0x898d020) at kernel/qcoreapplication.cpp:867
#43 in QCoreApplication::sendEvent (receiver=0x80d1a80, event=0x898d020) at ../../include/QtCore/../../src/corelib/kernel/qcoreapplication.h:232
#44 in QCoreApplicationPrivate::sendPostedEvents (receiver=0x0, event_type=0, data=0x8073318) at kernel/qcoreapplication.cpp:1471
#45 in QCoreApplication::sendPostedEvents (receiver=0x0, event_type=0) at kernel/qcoreapplication.cpp:1329
#46 in QWindowSystemInterface::sendWindowSystemEvents (flags=...) at kernel/qwindowsysteminterface.cpp:560
#47 in QUnixEventDispatcherQPA::processEvents (this=0x8079958, flags=...) at eventdispatchers/qunixeventdispatcher.cpp:70
#48 in QEventLoop::processEvents (this=0xbfffef50, flags=...) at kernel/qeventloop.cpp:136
#49 in QEventLoop::exec (this=0xbfffef50, flags=...) at kernel/qeventloop.cpp:212
#50 in QCoreApplication::exec () at kernel/qcoreapplication.cpp:1120
#51 in QGuiApplication::exec () at kernel/qguiapplication.cpp:1220
#52 in QApplication::exec () at kernel/qapplication.cpp:2689
#53 in main(argc=2, argv=0xbffff294)

UPDATE2:

回答这个问题的宝贵意见。 我还通过以下链接共享了一些详细的回溯文件:1drv.ms/f/s!AlojS_vldQMhjHRlTfU9vwErNz-H。请参考Readme.txt 对于一些解释和我使用的libc版本。 顺便说一句,当我尝试用vfork()/ waitpid()替换system()时,冻结似乎不再出现。我不知道原因。

谢谢大家。

3 个答案:

答案 0 :(得分:2)

没有提供源代码,很难确定性地回答这个问题。根据我在多线程程序中的经验,很容易忽略某个可能发生死锁的地方。就您而言,这听起来很像,这不太可能发生。但是我敢打赌,在您代码的某个地方,您可能会陷入僵局。

我建议您在图中绘制整个环境,并查看哪些线程使用哪些共享资源以及互斥锁何时何地进入。

但是正如我在开始时所说,没有更多信息,这很难说。

答案 1 :(得分:2)

从回溯来看,似乎在Qt试图构成事件时调用了malloc。

如果您尝试跨线程发送事件,则Qt可以为您排队事件。但是,如果这些事件没有耗尽也可能会耗尽您的内存。然后,您可以从malloc获得有线行为,因为没有剩余的内存。

  • 您是否有办法监视程序的内存使用情况,并查看每次内存满时是否会发生这种情况?
  • 您是否有办法减少系统的内存并查看是否经常出现此问题?

如果确实存在上述问题,那么您可以查看此thread作为解决方案。

答案 2 :(得分:0)

如果使用信号和插槽在线程之间进行通信,则应了解不同的连接模式。

  • 自动连接(默认),如果线程中发出信号 接收对象具有亲和力,则行为相同 作为直接连接。否则,行为与 排队的连接。
  • 直接连接插槽被调用 发出信号后立即。该插槽在 发射器的线程,不一定是接收器的线程。
  • 排队连接当控制权返回到 接收者线程的事件循环。该插槽在 接收者的线程。
  • 阻止排队连接插槽被调用为 对于排队连接,除了当前线程阻塞直到 广告位返回。注意:使用此类型连接同一对象 线程将导致死锁。

更多内容:https://doc.qt.io/archives/qt-5.6/threads-qobject.html

该问题确实需要一些代码上下文。当您将数据传递到UI时会发生这种现象吗?如果是,您正在使用QWidgets,QML等吗?将数据呈现到UI时,许多Qt模式都依赖于信号/插槽。