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会正确处理不同线程之间的连接。