Qt线程爬坑1- QBackingStore::endPaint() called with active painter 和 QWidget::repaint: Recursive repain

问题

本来只是写一个在子线程去请求网络,然后在通过信号槽与主线程通信的简单功能,示例代码如下:

//Worker.h
class Worker : public QObject
{
    Q_OBJECT
        
public:
    explicit Worker(QObject *parent = nullptr);
    ~Worker();
    
signals:
	void requestFial(QString strError, int code);
    void requestSuccess(QString strResponse);
};    
//MainWindow.cpp
class MainWindow::initData()
{
    m_thread = new QThread(this);
    m_worker = new Worker();
    m_worker->moveToThread();
    connect(m_worker, &Worker::requestFail, this, [this](QString strError, int code){
        //....
        QMessageBox::information(this, "请求失败", strError);
    });
    connect(m_worker, &Worker::requestSuccess, [this](QString strResponse){
       
      /*--------报错-------*/  
      //.....
       QMessageBox::information(this, "请求成功", strResponse); 
    });
}    

代码在请求成功后通过信号槽与主线程通信时就报错:QBackingStore::endPaint() called with active painter on backingstore paint

查了一下资料:

Qt 解决报错:QBackingStore::endPaint() called with active painter_0ices的博客-优快云博客

但是我代码里没有用到QPainter 啊,但是或许是控件里面调用的,于是在开启调试,再次运行程序时,直接崩溃了???报错:QWidget::repaint: Recursive repaint detected when updating progress bar

这个又去查了资料,这是在子线程更新UI导致的:

c++ - QWidget::repaint: Recursive repaint detected when updating progress bar - Stack Overflow

但是这更不可能了,我怎么可能会犯这种错误?耗时操作都放到子线程了,更新也是主线程完成的。

然后不断尝试就在这两种错误间反复横跳,一直查这两个错误相关的资料,也去检查自己更新的UI的部分,指定信号槽的连接方式等,但是然并卵。

问题解决

后面无奈拿出输出调试大法,输出主线程,子线程以及槽函数部分的线程ID,果然发现异常了

/*--------MainWindow.cpp-------------*/
connect(m_worker, &Worker::requestSuccess, [this](QString strResponse){
       
      /*--------报错-------*/  
      //.....
       qDebug() << "threadId" << QThread::currentThreadId();	
       QMessageBox::information(this, "请求成功", strResponse); 
    });

子线程的线程ID竟然和槽函数所在线程的ID一致!即槽函数的更新UI部分是在子线程里做的!所以才会导致崩溃。

然后又去检查这部分代码,终于发现自己竟然漏写了接受者this!!!

/*------------wrong-------------*/
connect(m_worker, &Worker::requestSuccess, [this](QString strResponse){
       
      /*--------报错-------*/  
      //.....
       qDebug() << "threadId" << QThread::currentThreadId();	
       QMessageBox::information(this, "请求成功", strResponse); 
    });


/*------------correct-----------*/
connect(m_worker, &Worker::requestSuccess,this, [this](QString strResponse){
       
      /*--------报错-------*/  
      //.....
       qDebug() << "threadId" << QThread::currentThreadId();	
       QMessageBox::information(this, "请求成功", strResponse); 
    });

但是这个编译竟然通过了,于是又去查QT帮助文档,找到了connect 的重载函数

QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const char *method, Qt::ConnectionType type = Qt::AutoConnection) const

文档对这个重载方法说明如下:

This function overloads connect().

Connects signal from the sender object to this object’s method.

Equivalent to connect(sender, signal, this, method, type).

说是等效于connect(sender, signal, this, method, type)

但是这个this 是指当前类的示例,还是指sender

于是又去做了试验,环境是win10+qt6.2.1+MinGW64

分别输出主线程ID,槽函数所在线程ID,槽函数捕获的this所在的线程ID和子线程ID

/*-----------------------Worker-----------------------------*/

void Worker::run()
{
    qDebug() << "子线程ID" << QThread::currentThreadId();
    emit requesetSuccess("请求成功!");
}    

/*------------------------MainWindow------------------------*/

qDebug() << "主线程ID" << QThread::currentThreadId();

m_thread = new QThread(this);
m_worker = new Worker();
m_worker->moveToThread(m_thread);
connect(m_thread, &QThread::started, m_worker, &Worker::run);
connect(m_worker, &Worker::requesetSuccess, this, [this](QString response) {
    qDebug() << "槽函数线程ID" << QThread::currentThreadId();
    qDebug() << "this线程ID" << this->thread()->currentThreadId();
    QMessageBox::information(this, "提示", response);
});
m_thread->start();

结果如下

主线程ID 0xd814
子线程ID 0xeb54
槽函数线程ID 0xd814
this线程ID 0xd814

结果一切正常。

修改connect部分代码

connect(m_worker, &Worker::requesetSuccess, this, [this](QString response) {
    qDebug() << "槽函数线程ID" << QThread::currentThreadId();
    qDebug() << "this线程ID" << this->thread()->currentThreadId();
    QMessageBox::information(this, "提示", response);
});

结果如下:

主线程ID 0xe46c
子线程ID 0xe208
槽函数线程ID 0xe208
this线程ID 0xe208
QObject::setParent: Cannot set parent, new parent is in a different thread
17:22:56: 程序异常结束。

槽函数是在子线程执行的这在上面已经验证过了,但是lambda表达式捕获的this竟然是子线程示例倒是我没想到的,之前一直以为自己捕获的是MainWindow实例。最终程序崩溃。

但是这个重载版本的connect 内部原理还有待深入理解。#TODO

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值