Qt:从后台线程发出信号安全吗?
Qt: is it safe to emit signals from background thread?
我需要从后台线程跟踪我的计算进度。
我是这样做的:(摘自官方文档)
void MainWindow::startCalculation()
{
//Progress bar
ui->cmdLogX->setText(tr("Расчёт..."));
ui->cmdLogX->setEnabled(false);
//Create a new Thread
Worker *worker = new Worker(nullptr, 0.0, 100, 0.1);
worker->moveToThread(&background);
connect(&background, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &MainWindow::runCalculation, worker, &Worker::run);
connect(worker, &Worker::calculated, this, &MainWindow::plotComputedData);
connect(worker, &Worker::reportProgress,
[this] (int x) {
bar->setValue(x);});
background.start(QThread::HighPriority);
emit runCalculation();
}
这里是Worker::run()
void Worker::run()
{
if (_data.size() > 0)
_data.clear();
int i = 0;
for (double x = _min; x < _max; x += _step, ++i)
{
_data.push_back(QPointF(x, getY(x)));
//emit reportProgress(i);
QThread::msleep(10);
}
emit calculated(_data);
}
如果我发出 reportProgress(i),应用程序会因段错误而崩溃(在不同的函数中,上次是 QAnimationTimer::updateAnimationsTime。
MWE:https://www.dropbox.com/s/thgxwc98fevi6v3/qwt-test2.tar.bz2?dl=0
编辑:蓝牙:
(gdb) bt
#0 0x0000000100caca62 in QAnimationTimer::updateAnimationsTime(long long) () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#1 0x0000000100cab5f7 in QUnifiedTimer::updateAnimationTimers(long long) () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#2 0x0000000100cad9fc in QAnimationDriver::advance() () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#3 0x0000000100f04300 in QObject::event(QEvent*) () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#4 0x0000000100061dbb in QApplicationPrivate::notify_helper(QObject*, QEvent*) () from /Users/pasha/Qt/5.4/clang_64/lib/QtWidgets.framework/Versions/5/QtWidgets
#5 0x0000000100065110 in QApplication::notify(QObject*, QEvent*) () from /Users/pasha/Qt/5.4/clang_64/lib/QtWidgets.framework/Versions/5/QtWidgets
#6 0x0000000100ed8fb3 in QCoreApplication::notifyInternal(QObject*, QEvent*) () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#7 0x0000000100f2d3f6 in QTimerInfoList::activateTimers() () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#8 0x0000000105799d12 in QCocoaEventDispatcherPrivate::activateTimersSourceCallback(void*) () from /Users/pasha/Qt/5.4/clang_64/plugins/platforms/libqcocoa.dylib
#9 0x00007fff88cb2681 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ () from /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
#10 0x00007fff88ca48dc in __CFRunLoopDoSources0 () from /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
#11 0x00007fff88ca3e3f in __CFRunLoopRun () from /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
#12 0x00007fff88ca3858 in CFRunLoopRunSpecific () from /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
#13 0x00007fff8b9daaef in RunCurrentEventLoopInMode () from /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox
#14 0x00007fff8b9da86a in ReceiveNextEventCommon () from /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox
#15 0x00007fff8b9da6ab in _BlockUntilNextEventMatchingListInModeWithFilter () from /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox
#16 0x00007fff91beff81 in _DPSNextEvent () from /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit
#17 0x00007fff91bef730 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] () from /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit
#18 0x00007fff91be3593 in -[NSApplication run] () from /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit
#19 0x000000010579a92d in QCocoaEventDispatcher::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /Users/pasha/Qt/5.4/clang_64/plugins/platforms/libqcocoa.dylib
#20 0x0000000100ed65ad in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#21 0x0000000100ed958a in QCoreApplication::exec() () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#22 0x0000000100003c69 in main (argc=1, argv=0x7fff5fbffa78) at ../qwt-test2/main.cpp:12
(gdb)
#0 0x0000000100caca62 in QAnimationTimer::updateAnimationsTime(long long) () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#1 0x0000000100cab5f7 in QUnifiedTimer::updateAnimationTimers(long long) () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#2 0x0000000100cad9fc in QAnimationDriver::advance() () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#3 0x0000000100f04300 in QObject::event(QEvent*) () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#4 0x0000000100061dbb in QApplicationPrivate::notify_helper(QObject*, QEvent*) () from /Users/pasha/Qt/5.4/clang_64/lib/QtWidgets.framework/Versions/5/QtWidgets
#5 0x0000000100065110 in QApplication::notify(QObject*, QEvent*) () from /Users/pasha/Qt/5.4/clang_64/lib/QtWidgets.framework/Versions/5/QtWidgets
#6 0x0000000100ed8fb3 in QCoreApplication::notifyInternal(QObject*, QEvent*) () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#7 0x0000000100f2d3f6 in QTimerInfoList::activateTimers() () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#8 0x0000000105799d12 in QCocoaEventDispatcherPrivate::activateTimersSourceCallback(void*) () from /Users/pasha/Qt/5.4/clang_64/plugins/platforms/libqcocoa.dylib
#9 0x00007fff88cb2681 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ () from /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
#10 0x00007fff88ca48dc in __CFRunLoopDoSources0 () from /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
#11 0x00007fff88ca3e3f in __CFRunLoopRun () from /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
#12 0x00007fff88ca3858 in CFRunLoopRunSpecific () from /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
#13 0x00007fff8b9daaef in RunCurrentEventLoopInMode () from /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox
#14 0x00007fff8b9da86a in ReceiveNextEventCommon () from /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox
#15 0x00007fff8b9da6ab in _BlockUntilNextEventMatchingListInModeWithFilter () from /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox
#16 0x00007fff91beff81 in _DPSNextEvent () from /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit
#17 0x00007fff91bef730 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] () from /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit
#18 0x00007fff91be3593 in -[NSApplication run] () from /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit
#19 0x000000010579a92d in QCocoaEventDispatcher::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /Users/pasha/Qt/5.4/clang_64/plugins/platforms/libqcocoa.dylib
#20 0x0000000100ed65ad in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#21 0x0000000100ed958a in QCoreApplication::exec() () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#22 0x0000000100003c69 in main (argc=1, argv=0x7fff5fbffa78) at ../qwt-test2/main.cpp:12
您正试图在非主线程中使用 UI 组件,这在 Qt 中是被禁止的。为了修复它,您可以这样做:
connect(worker, &Worker::reportProgress, this,
[this] (int x) {
bar->setValue(x);
}, Qt::QueuedConnection);
当你这样做时:
connect(worker, &Worker::reportProgress,
[this] (int x) {
bar->setValue(x);});
您没有将信号连接到槽,而是直接连接到函数指针。 Qt 无法检查连接是否应该排队,并从不同的线程调用线程不安全代码 (GUI)
不使用 lambda,而是在 QMainWindow 中创建一个用于更新条形图的槽,并将线程信号连接到该槽。届时Qt会正确处理不同线程之间的连接。
我需要从后台线程跟踪我的计算进度。
我是这样做的:(摘自官方文档)
void MainWindow::startCalculation()
{
//Progress bar
ui->cmdLogX->setText(tr("Расчёт..."));
ui->cmdLogX->setEnabled(false);
//Create a new Thread
Worker *worker = new Worker(nullptr, 0.0, 100, 0.1);
worker->moveToThread(&background);
connect(&background, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &MainWindow::runCalculation, worker, &Worker::run);
connect(worker, &Worker::calculated, this, &MainWindow::plotComputedData);
connect(worker, &Worker::reportProgress,
[this] (int x) {
bar->setValue(x);});
background.start(QThread::HighPriority);
emit runCalculation();
}
这里是Worker::run()
void Worker::run()
{
if (_data.size() > 0)
_data.clear();
int i = 0;
for (double x = _min; x < _max; x += _step, ++i)
{
_data.push_back(QPointF(x, getY(x)));
//emit reportProgress(i);
QThread::msleep(10);
}
emit calculated(_data);
}
如果我发出 reportProgress(i),应用程序会因段错误而崩溃(在不同的函数中,上次是 QAnimationTimer::updateAnimationsTime。
MWE:https://www.dropbox.com/s/thgxwc98fevi6v3/qwt-test2.tar.bz2?dl=0
编辑:蓝牙:
(gdb) bt
#0 0x0000000100caca62 in QAnimationTimer::updateAnimationsTime(long long) () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#1 0x0000000100cab5f7 in QUnifiedTimer::updateAnimationTimers(long long) () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#2 0x0000000100cad9fc in QAnimationDriver::advance() () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#3 0x0000000100f04300 in QObject::event(QEvent*) () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#4 0x0000000100061dbb in QApplicationPrivate::notify_helper(QObject*, QEvent*) () from /Users/pasha/Qt/5.4/clang_64/lib/QtWidgets.framework/Versions/5/QtWidgets
#5 0x0000000100065110 in QApplication::notify(QObject*, QEvent*) () from /Users/pasha/Qt/5.4/clang_64/lib/QtWidgets.framework/Versions/5/QtWidgets
#6 0x0000000100ed8fb3 in QCoreApplication::notifyInternal(QObject*, QEvent*) () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#7 0x0000000100f2d3f6 in QTimerInfoList::activateTimers() () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#8 0x0000000105799d12 in QCocoaEventDispatcherPrivate::activateTimersSourceCallback(void*) () from /Users/pasha/Qt/5.4/clang_64/plugins/platforms/libqcocoa.dylib
#9 0x00007fff88cb2681 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ () from /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
#10 0x00007fff88ca48dc in __CFRunLoopDoSources0 () from /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
#11 0x00007fff88ca3e3f in __CFRunLoopRun () from /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
#12 0x00007fff88ca3858 in CFRunLoopRunSpecific () from /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
#13 0x00007fff8b9daaef in RunCurrentEventLoopInMode () from /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox
#14 0x00007fff8b9da86a in ReceiveNextEventCommon () from /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox
#15 0x00007fff8b9da6ab in _BlockUntilNextEventMatchingListInModeWithFilter () from /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox
#16 0x00007fff91beff81 in _DPSNextEvent () from /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit
#17 0x00007fff91bef730 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] () from /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit
#18 0x00007fff91be3593 in -[NSApplication run] () from /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit
#19 0x000000010579a92d in QCocoaEventDispatcher::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /Users/pasha/Qt/5.4/clang_64/plugins/platforms/libqcocoa.dylib
#20 0x0000000100ed65ad in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#21 0x0000000100ed958a in QCoreApplication::exec() () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#22 0x0000000100003c69 in main (argc=1, argv=0x7fff5fbffa78) at ../qwt-test2/main.cpp:12
(gdb)
#0 0x0000000100caca62 in QAnimationTimer::updateAnimationsTime(long long) () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#1 0x0000000100cab5f7 in QUnifiedTimer::updateAnimationTimers(long long) () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#2 0x0000000100cad9fc in QAnimationDriver::advance() () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#3 0x0000000100f04300 in QObject::event(QEvent*) () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#4 0x0000000100061dbb in QApplicationPrivate::notify_helper(QObject*, QEvent*) () from /Users/pasha/Qt/5.4/clang_64/lib/QtWidgets.framework/Versions/5/QtWidgets
#5 0x0000000100065110 in QApplication::notify(QObject*, QEvent*) () from /Users/pasha/Qt/5.4/clang_64/lib/QtWidgets.framework/Versions/5/QtWidgets
#6 0x0000000100ed8fb3 in QCoreApplication::notifyInternal(QObject*, QEvent*) () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#7 0x0000000100f2d3f6 in QTimerInfoList::activateTimers() () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#8 0x0000000105799d12 in QCocoaEventDispatcherPrivate::activateTimersSourceCallback(void*) () from /Users/pasha/Qt/5.4/clang_64/plugins/platforms/libqcocoa.dylib
#9 0x00007fff88cb2681 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ () from /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
#10 0x00007fff88ca48dc in __CFRunLoopDoSources0 () from /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
#11 0x00007fff88ca3e3f in __CFRunLoopRun () from /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
#12 0x00007fff88ca3858 in CFRunLoopRunSpecific () from /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
#13 0x00007fff8b9daaef in RunCurrentEventLoopInMode () from /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox
#14 0x00007fff8b9da86a in ReceiveNextEventCommon () from /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox
#15 0x00007fff8b9da6ab in _BlockUntilNextEventMatchingListInModeWithFilter () from /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox
#16 0x00007fff91beff81 in _DPSNextEvent () from /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit
#17 0x00007fff91bef730 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] () from /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit
#18 0x00007fff91be3593 in -[NSApplication run] () from /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit
#19 0x000000010579a92d in QCocoaEventDispatcher::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /Users/pasha/Qt/5.4/clang_64/plugins/platforms/libqcocoa.dylib
#20 0x0000000100ed65ad in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#21 0x0000000100ed958a in QCoreApplication::exec() () from /Users/pasha/Qt/5.4/clang_64/lib/QtCore.framework/Versions/5/QtCore
#22 0x0000000100003c69 in main (argc=1, argv=0x7fff5fbffa78) at ../qwt-test2/main.cpp:12
您正试图在非主线程中使用 UI 组件,这在 Qt 中是被禁止的。为了修复它,您可以这样做:
connect(worker, &Worker::reportProgress, this,
[this] (int x) {
bar->setValue(x);
}, Qt::QueuedConnection);
当你这样做时:
connect(worker, &Worker::reportProgress,
[this] (int x) {
bar->setValue(x);});
您没有将信号连接到槽,而是直接连接到函数指针。 Qt 无法检查连接是否应该排队,并从不同的线程调用线程不安全代码 (GUI)
不使用 lambda,而是在 QMainWindow 中创建一个用于更新条形图的槽,并将线程信号连接到该槽。届时Qt会正确处理不同线程之间的连接。