分享一篇技术文章,从概念原理及应用分析QEventLoop问题,其他qt工程师遇到此类问题可以快速解决、提高软件质量。
事件循环本质上就是一个无限循环,不停地去获取下一个事件,然后做出处理;直到 quit 事件发生,循环结束。QEventLoop事件循环,调用exec();会被阻塞。直到while循环退出。
QEventLoop用不好会出现代码异常、程序卡死、崩溃的现象。qt封装了很多,不知道原理就去用?出现问题碰运气解决或者不解决?
猜想,qt多线程的信号槽原理是多线程的生产者消费者模型。QEventLoop::exec();事件循环处理本线程所有事件。
一个错误的例子,本例中Slot1();槽函数代码“loop.exec(QEventLoop::ExcludeUserInputEvents);”后面的流程没有处理到,出现Slot1();在exec();中调用其他槽函数(这里例子指Slot1();递归)的现象。
引用:QT信号槽机制分析_同一个槽函数避免覆盖的问题-CSDN博客
(7).活用慎用processEvents,线程(包括主线程和其它线程)执行很繁重的计算任务中,为防止消息事件一直无机会处理,则在函数中手动调用processEvents,让消息事件有机会更新。界面不会假死。另一方面,槽函数中不可调用processEvents,因为这会导致“中断”当前槽函数,进而去执行消息队列中后续的槽函数,可能会引发同一槽函数重入问题,将编程问题复杂化。
————————————————
版权声明:本文为CSDN博主「抬头仰望的天空」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lo_ve18/article/details/115693667
我的例子与分析:
#pragma once
#include
#include "ui_QtWidgetsApplication1.h"
class QtWidgetsApplication1 : public QWidget
{
Q_OBJECT
public:
QtWidgetsApplication1(QWidget *parent = nullptr);
~QtWidgetsApplication1();
signals:
void Sig1();
private slots:
void Slot1();
void on_pushButton_clicked();
private:
Ui::QtWidgetsApplication1Class ui;
};
#include "QtWidgetsApplication1.h"
#include
#include
#include
#include
#include
#include
QtWidgetsApplication1::QtWidgetsApplication1(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
connect(this, SIGNAL(Sig1()), this, SLOT(Slot1()), Qt::QueuedConnection);
//模拟工作者线程通知有需要获取网络数据的情况,时间频率不定
std::thread([this]() {
while (1)
{
emit Sig1();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}).detach();
}
QtWidgetsApplication1::~QtWidgetsApplication1()
{}
void QtWidgetsApplication1::Slot1()
{
//本义以阻塞模式获取网络数据,超时值5s,允许ui卡顿
QEventLoop loop;
//其他网络请求代码与loop以信号槽的形式绑定,代码省略
QTimer::singleShot(5000, &loop, SLOT(quit()));//网络请求超时实现
loop.exec(QEventLoop::ExcludeUserInputEvents);//排除用户鼠标键盘事件
//其他业务流程代码...
static int i = 0;
qDebug() << __FUNCTION__ << "," << i++;
}
void QtWidgetsApplication1::on_pushButton_clicked()
{
}
发现其他槽函数调用递归于loop.exec(QEventLoop::ExcludeUserInputEvents);,调用堆栈:
QtWidgetsApplication1.exe!QtWidgetsApplication1::Slot1() 行 27 C++
QtWidgetsApplication1.exe!QtWidgetsApplication1::qt_static_metacall(QObject * _o, QMetaObject::Call _c, int _id, void * * _a) 行 84 C++
Qt5Cored.dll!QMetaCallEvent::placeMetaCall(QObject * object) 行 618 C++
Qt5Cored.dll!QObject::event(QEvent * e) 行 1314 C++
Qt5Widgetsd.dll!QWidget::event(QEvent * event) 行 9080 C++
Qt5Widgetsd.dll!QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e) 行 3632 C++
Qt5Widgetsd.dll!QApplication::notify(QObject * receiver, QEvent * e) 行 3582 C++
Qt5Cored.dll!QCoreApplication::notifyInternal2(QObject * receiver, QEvent * event) 行 1063 C++
Qt5Cored.dll!QCoreApplication::sendEvent(QObject * receiver, QEvent * event) 行 1459 C++
Qt5Cored.dll!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver, int event_type, QThreadData * data) 行 1817 C++
Qt5Cored.dll!QEventDispatcherWin32::sendPostedEvents() 行 1082 C++
qwindowsd.dll!QWindowsGuiEventDispatcher::sendPostedEvents() 行 81 C++
Qt5Cored.dll!QEventDispatcherWin32::processEvents(QFlags flags) 行 530 C++
qwindowsd.dll!QWindowsGuiEventDispatcher::processEvents(QFlags flags) 行 73 C++
Qt5Cored.dll!QEventLoop::processEvents(QFlags flags) 行 140 C++
Qt5Cored.dll!QEventLoop::exec(QFlags flags) 行 232 C++
QtWidgetsApplication1.exe!QtWidgetsApplication1::Slot1() 行 33 C++
QtWidgetsApplication1.exe!QtWidgetsApplication1::qt_static_metacall(QObject * _o, QMetaObject::Call _c, int _id, void * * _a) 行 84 C++
Qt5Cored.dll!QMetaCallEvent::placeMetaCall(QObject * object) 行 618 C++
Qt5Cored.dll!QObject::event(QEvent * e) 行 1314 C++
Qt5Widgetsd.dll!QWidget::event(QEvent * event) 行 9080 C++
Qt5Widgetsd.dll!QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e) 行 3632 C++
Qt5Widgetsd.dll!QApplication::notify(QObject * receiver, QEvent * e) 行 3582 C++
Qt5Cored.dll!QCoreApplication::notifyInternal2(QObject * receiver, QEvent * event) 行 1063 C++
Qt5Cored.dll!QCoreApplication::sendEvent(QObject * receiver, QEvent * event) 行 1459 C++
Qt5Cored.dll!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver, int event_type, QThreadData * data) 行 1817 C++
Qt5Cored.dll!QEventDispatcherWin32::sendPostedEvents() 行 1082 C++
qwindowsd.dll!QWindowsGuiEventDispatcher::sendPostedEvents() 行 81 C++
Qt5Cored.dll!QEventDispatcherWin32::processEvents(QFlags flags) 行 530 C++
qwindowsd.dll!QWindowsGuiEventDispatcher::processEvents(QFlags flags) 行 73 C++
Qt5Cored.dll!QEventLoop::processEvents(QFlags flags) 行 140 C++
Qt5Cored.dll!QEventLoop::exec(QFlags flags) 行 232 C++
QtWidgetsApplication1.exe!QtWidgetsApplication1::Slot1() 行 33 C++
QtWidgetsApplication1.exe!QtWidgetsApplication1::qt_static_metacall(QObject * _o, QMetaObject::Call _c, int _id, void * * _a) 行 84 C++
Qt5Cored.dll!QMetaCallEvent::placeMetaCall(QObject * object) 行 618 C++
Qt5Cored.dll!QObject::event(QEvent * e) 行 1314 C++
Qt5Widgetsd.dll!QWidget::event(QEvent * event) 行 9080 C++
Qt5Widgetsd.dll!QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e) 行 3632 C++
Qt5Widgetsd.dll!QApplication::notify(QObject * receiver, QEvent * e) 行 3582 C++
Qt5Cored.dll!QCoreApplication::notifyInternal2(QObject * receiver, QEvent * event) 行 1063 C++
Qt5Cored.dll!QCoreApplication::sendEvent(QObject * receiver, QEvent * event) 行 1459 C++
Qt5Cored.dll!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver, int event_type, QThreadData * data) 行 1817 C++
Qt5Cored.dll!QEventDispatcherWin32::sendPostedEvents() 行 1082 C++
qwindowsd.dll!QWindowsGuiEventDispatcher::sendPostedEvents() 行 81 C++
Qt5Cored.dll!QEventDispatcherWin32::processEvents(QFlags flags) 行 530 C++
qwindowsd.dll!QWindowsGuiEventDispatcher::processEvents(QFlags flags) 行 73 C++
Qt5Cored.dll!QEventLoop::processEvents(QFlags flags) 行 140 C++
Qt5Cored.dll!QEventLoop::exec(QFlags flags) 行 232 C++
QtWidgetsApplication1.exe!QtWidgetsApplication1::Slot1() 行 33 C++
QtWidgetsApplication1.exe!QtWidgetsApplication1::qt_static_metacall(QObject * _o, QMetaObject::Call _c, int _id, void * * _a) 行 84 C++
Qt5Cored.dll!QMetaCallEvent::placeMetaCall(QObject * object) 行 618 C++
Qt5Cored.dll!QObject::event(QEvent * e) 行 1314 C++
Qt5Widgetsd.dll!QWidget::event(QEvent * event) 行 9080 C++
Qt5Widgetsd.dll!QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e) 行 3632 C++
Qt5Widgetsd.dll!QApplication::notify(QObject * receiver, QEvent * e) 行 3582 C++
Qt5Cored.dll!QCoreApplication::notifyInternal2(QObject * receiver, QEvent * event) 行 1063 C++
Qt5Cored.dll!QCoreApplication::sendEvent(QObject * receiver, QEvent * event) 行 1459 C++
Qt5Cored.dll!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver, int event_type, QThreadData * data) 行 1817 C++
Qt5Cored.dll!QEventDispatcherWin32::sendPostedEvents() 行 1082 C++
qwindowsd.dll!QWindowsGuiEventDispatcher::sendPostedEvents() 行 81 C++
Qt5Cored.dll!QEventDispatcherWin32::processEvents(QFlags flags) 行 530 C++
qwindowsd.dll!QWindowsGuiEventDispatcher::processEvents(QFlags flags) 行 73 C++
Qt5Cored.dll!QEventLoop::processEvents(QFlags flags) 行 140 C++
Qt5Cored.dll!QEventLoop::exec(QFlags flags) 行 232 C++
QtWidgetsApplication1.exe!QtWidgetsApplication1::Slot1() 行 33 C++
QtWidgetsApplication1.exe!QtWidgetsApplication1::qt_static_metacall(QObject * _o, QMetaObject::Call _c, int _id, void * * _a) 行 84 C++
Qt5Cored.dll!QMetaCallEvent::placeMetaCall(QObject * object) 行 618 C++
Qt5Cored.dll!QObject::event(QEvent * e) 行 1314 C++
Qt5Widgetsd.dll!QWidget::event(QEvent * event) 行 9080 C++
Qt5Widgetsd.dll!QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e) 行 3632 C++
Qt5Widgetsd.dll!QApplication::notify(QObject * receiver, QEvent * e) 行 3582 C++
Qt5Cored.dll!QCoreApplication::notifyInternal2(QObject * receiver, QEvent * event) 行 1063 C++
Qt5Cored.dll!QCoreApplication::sendEvent(QObject * receiver, QEvent * event) 行 1459 C++
Qt5Cored.dll!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver, int event_type, QThreadData * data) 行 1817 C++
> Qt5Cored.dll!QEventDispatcherWin32::sendPostedEvents() 行 1082 C++
qwindowsd.dll!QWindowsGuiEventDispatcher::sendPostedEvents() 行 81 C++
Qt5Cored.dll!QEventDispatcherWin32::processEvents(QFlags flags) 行 530 C++
qwindowsd.dll!QWindowsGuiEventDispatcher::processEvents(QFlags flags) 行 73 C++
Qt5Cored.dll!QEventLoop::processEvents(QFlags flags) 行 140 C++
Qt5Cored.dll!QEventLoop::exec(QFlags flags) 行 232 C++
QtWidgetsApplication1.exe!QtWidgetsApplication1::Slot1() 行 33 C++
QtWidgetsApplication1.exe!QtWidgetsApplication1::qt_static_metacall(QObject * _o, QMetaObject::Call _c, int _id, void * * _a) 行 84 C++
Qt5Cored.dll!QMetaCallEvent::placeMetaCall(QObject * object) 行 618 C++
Qt5Cored.dll!QObject::event(QEvent * e) 行 1314 C++
Qt5Widgetsd.dll!QWidget::event(QEvent * event) 行 9080 C++
Qt5Widgetsd.dll!QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e) 行 3632 C++
Qt5Widgetsd.dll!QApplication::notify(QObject * receiver, QEvent * e) 行 3582 C++
Qt5Cored.dll!QCoreApplication::notifyInternal2(QObject * receiver, QEvent * event) 行 1063 C++
Qt5Cored.dll!QCoreApplication::sendEvent(QObject * receiver, QEvent * event) 行 1459 C++
Qt5Cored.dll!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver, int event_type, QThreadData * data) 行 1817 C++
Qt5Cored.dll!QEventDispatcherWin32::sendPostedEvents() 行 1082 C++
qwindowsd.dll!QWindowsGuiEventDispatcher::sendPostedEvents() 行 81 C++
Qt5Cored.dll!QEventDispatcherWin32::processEvents(QFlags flags) 行 530 C++
qwindowsd.dll!QWindowsGuiEventDispatcher::processEvents(QFlags flags) 行 73 C++
Qt5Cored.dll!QEventLoop::processEvents(QFlags flags) 行 140 C++
Qt5Cored.dll!QEventLoop::exec(QFlags flags) 行 232 C++
Qt5Cored.dll!QCoreApplication::exec() 行 1371 C++
Qt5Guid.dll!QGuiApplication::exec() 行 1868 C++
Qt5Widgetsd.dll!QApplication::exec() 行 2825 C++
QtWidgetsApplication1.exe!main(int argc, char * * argv) 行 9 C++
QtWidgetsApplication1.exe!WinMain(HINSTANCE__ * __formal, HINSTANCE__ * __formal, char * __formal, int __formal) 行 97 C++
生产者线程投递产品:
Qt5Cored.dll!QCoreApplication::postEvent(QObject * receiver, QEvent * event, int priority) 行 1587 C++
Qt5Cored.dll!queued_activate(QObject * sender, int signal, QObjectPrivate::Connection * c, void * * argv) 行 3762 C++
Qt5Cored.dll!doActivate<0>(QObject * sender, int signal_index, void * * argv) 行 3847 C++
Qt5Cored.dll!QMetaObject::activate(QObject * sender, const QMetaObject * m, int local_signal_index, void * * argv) 行 3947 C++
QtWidgetsApplication1.exe!QtWidgetsApplication1::Sig1() 行 145 C++
> QtWidgetsApplication1.exe!`QtWidgetsApplication1::QtWidgetsApplication1'::`2'::::operator()() 行 17 C++
选择投递接收者线程,本线程:
QCoreApplicationPrivate::QPostEventListLocker QCoreApplicationPrivate::lockThreadPostEventList(QObject *object)
{
QPostEventListLocker locker;
if (!object) {
locker.threadData = QThreadData::current();
locker.locker = qt_unique_lock(locker.threadData->postEventList.mutex);
return locker;
}
投递:
void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority)
{
Q_TRACE_SCOPE(QCoreApplication_postEvent, receiver, event, event->type());
if (receiver == nullptr) {
qWarning("QCoreApplication::postEvent: Unexpected null receiver");
delete event;
return;
}
auto locker = QCoreApplicationPrivate::lockThreadPostEventList(receiver);
if (!locker.threadData) {
// posting during destruction? just delete the event to prevent a leak
delete event;
return;
}
QThreadData *data = locker.threadData;
// if this is one of the compressible events, do compression
if (receiver->d_func()->postedEvents
&& self && self->compressEvent(event, receiver, &data->postEventList)) {
Q_TRACE(QCoreApplication_postEvent_event_compressed, receiver, event);
return;
}
if (event->type() == QEvent::DeferredDelete)
receiver->d_ptr->deleteLaterCalled = true;
if (event->type() == QEvent::DeferredDelete && data == QThreadData::current()) {
int loopLevel = data->loopLevel;
int scopeLevel = data->scopeLevel;
if (scopeLevel == 0 && loopLevel != 0)
scopeLevel = 1;
static_cast(event)->level = loopLevel + scopeLevel;
}
// delete the event on exceptions to protect against memory leaks till the event is
// properly owned in the postEventList
QScopedPointer eventDeleter(event);
Q_TRACE(QCoreApplication_postEvent_event_posted, receiver, event, event->type());
data->postEventList.addEvent(QPostEvent(receiver, event, priority));
eventDeleter.take();
event->posted = true;
++receiver->d_func()->postedEvents;
data->canWait = false;
locker.unlock();
消费者线程(ui线程)取产品:
D:\Qt\5.15.2\Src\qtbase\src\corelib\kernel\qcoreapplication.cpp
void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type,
QThreadData *data)
{
if (event_type == -1) {
// we were called by an obsolete event dispatcher.
event_type = 0;
}
if (receiver && receiver->d_func()->threadData != data) {
qWarning("QCoreApplication::sendPostedEvents: Cannot send "
"posted events for objects in another thread");
return;
}
++data->postEventList.recursion;
auto locker = qt_unique_lock(data->postEventList.mutex);
// by default, we assume that the event dispatcher can go to sleep after
// processing all events. if any new events are posted while we send
// events, canWait will be set to false.
data->canWait = (data->postEventList.size() == 0);
if (data->postEventList.size() == 0 || (receiver && !receiver->d_func()->postedEvents)) {
--data->postEventList.recursion;
return;
}
data->canWait = true;
// okay. here is the tricky loop. be careful about optimizing
// this, it looks the way it does for good reasons.
int startOffset = data->postEventList.startOffset;
int &i = (!event_type && !receiver) ? data->postEventList.startOffset : startOffset;
data->postEventList.insertionOffset = data->postEventList.size();
// Exception-safe cleaning up without the need for a try/catch block
struct CleanUp {
QObject *receiver;
int event_type;
QThreadData *data;
bool exceptionCaught;
inline CleanUp(QObject *receiver, int event_type, QThreadData *data) :
receiver(receiver), event_type(event_type), data(data), exceptionCaught(true)
{}
inline ~CleanUp()
{
if (exceptionCaught) {
// since we were interrupted, we need another pass to make sure we clean everything up
data->canWait = false;
}
--data->postEventList.recursion;
if (!data->postEventList.recursion && !data->canWait && data->hasEventDispatcher())
data->eventDispatcher.loadRelaxed()->wakeUp();
// clear the global list, i.e. remove everything that was
// delivered.
if (!event_type && !receiver && data->postEventList.startOffset >= 0) {
const QPostEventList::iterator it = data->postEventList.begin();
data->postEventList.erase(it, it + data->postEventList.startOffset);
data->postEventList.insertionOffset -= data->postEventList.startOffset;
Q_ASSERT(data->postEventList.insertionOffset >= 0);
data->postEventList.startOffset = 0;
}
}
};
CleanUp cleanup(receiver, event_type, data);
while (i < data->postEventList.size()) {
// avoid live-lock
if (i >= data->postEventList.insertionOffset)
break;
const QPostEvent &pe = data->postEventList.at(i);
++i;
QEventLoop接管了执行代码所在线程的event事件循环,所以不应调用在槽函数中,若必须在槽函数中调用结合上面的原理,可以这样实现,接修改上面的例子:
void QtWidgetsApplication1::Slot1()
{
//本义以阻塞模式获取网络数据,超时值5s,允许ui卡顿
std::thread([]() {
QEventLoop loop;
//其他网络请求代码与loop以信号槽的形式绑定,代码省略
QTimer::singleShot(5000, &loop, SLOT(quit()));//网络请求超时实现
loop.exec(QEventLoop::ExcludeUserInputEvents);//排除用户鼠标键盘事件
}).join();
//其他业务流程代码...
static int i = 0;
qDebug() << __FUNCTION__ << "," << i++;
}
qt工作、学习中需要源码调试,qt5.15.2+vs2019源码调试开发环境搭建-CSDN博客