Многопоточное приложение Qt зависает, и несколько потоков ожидают один и тот же мьютекс - PullRequest
0 голосов
/ 01 марта 2019

Я столкнулся со странной проблемой с моим многопоточным приложением на основе 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:

Я могу предоставить обратную трассировку, но извините, я должен удалить некоторую конфиденциальную информацию.

Backtrace of sub thread:

#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)

Backtrace of main thread:

#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, которую я использовал.Кстати, когда я попытался заменить system () на vfork () / waitpid (), зависание, похоже, больше не появляется.Я не знал причину.

Спасибо всем заранее.

Ответы [ 3 ]

0 голосов
/ 05 марта 2019

Судя по трассировке, похоже, что malloc был вызван, когда Qt пытался опубликовать событие.

Если вы пытаетесь отправлять события между потоками, Qt может поставить вас в очередь.Но эти события могут заполнить вашу память, если она не истощена.Тогда вы можете получить проводное поведение от malloc, потому что не осталось памяти.

  • У вас есть возможность контролировать использование памяти вашей программой и видеть, происходит ли это каждый раз, когда память заполняется?
  • Есть ли у вас способ уменьшить объем памяти, который есть в системе, и посмотреть, возникает ли эта проблема чаще?

Если приведенное выше действительно является проблемой, то вы можете взятьпосмотрите на этот поток для решения.

0 голосов
/ 12 марта 2019

Если вы используете сигналы и слоты для связи между потоками, вы должны понимать различные схемы подключения.

  • Автоподключение (по умолчанию) Если сигнал излучается в потоке, к которому у принимающего объекта есть сходство, то поведение такое же, как и у Прямого соединения.В остальном поведение такое же, как и в очереди.
  • Прямое подключение Слот вызывается немедленно, когда излучается сигнал.Слот выполняется в потоке эмиттера, который не обязательно является потоком получателя.
  • Соединение в очереди Слот вызывается, когда управление возвращается в цикл событий потока получателя.Слот выполняется в потоке получателя.
  • Блокирование соединения в очереди Слот вызывается так же, как и для соединения с очередями, за исключением того, что текущий поток блокируется до тех пор, пока слот не вернется.Примечание. Использование этого типа для соединения объектов в одном потоке вызовет взаимоблокировку.

Подробнее здесь: https://doc.qt.io/archives/qt-5.6/threads-qobject.html

Однако вопрос требует некоторого контекста кода.Это происходит, когда вы передаете данные в пользовательский интерфейс?Если да, используете ли вы QWidgets, QML, ...?Многие шаблоны Qt полагаются на сигналы / слоты при рендеринге данных в пользовательский интерфейс.

0 голосов
/ 01 марта 2019

Без исходного кода трудно ответить на вопрос окончательно.По моему опыту с многопоточными программами действительно легко не заметить место, где может возникнуть тупик.В вашем случае это звучит как что-то, что вряд ли произойдет.Однако могу поспорить, что где-то в вашем коде у вас есть потенциальная тупиковая ситуация.

Я бы посоветовал вам нарисовать всю среду на диаграмме и посмотреть, какие потоки используют, какие общие ресурсы и когда и где мьютексывходите.

Но, как я сказал в начале, без дополнительной информации трудно сказать.

...