Qt中如何做到主线程更新ui,子线程进行耗时操作(面试问题)

如果ui更新出现了卡顿,那主要是因为GUI线程函数进行了耗时操作,sleep或者wait
所以一般处理是ui只用来收集参数,发送和接受信号,以及更新ui,而那些耗时操作是放在Worker类中,再通过MovetoThread()移动到子线程,然后主线程通过QueuedConnection调用Worker的槽函数
主线程不能通过wait()去等待子线程,而工作子线程也不能操纵ui
下面有个例子,涉及到线程的开始、停止、销毁,以及子线程中的休眠
Worker类

点击查看代码
#include "worker.h"

Worker::Worker(QObject *parent)
    : QObject{parent}
{

}

void Worker::processedData(QVector<int>vv)
{
    QVector<int>temp;
    temp.resize(vv.size());
    for(int i=0;i<temp.size();i++)
    {
        //查看当前线程是否有中断请求标记
        if(QThread::currentThread()->isInterruptionRequested())
        {
            qDebug()<<"任务取消";
            emit tastcancel();
            return;
        }
        if(i%20==0)
        {
            qDebug()<<i;
            emit is_twenty(i);
            QThread::msleep(100);
        }
    }
}

**主类**
点击查看代码
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->m_thread=new QThread();
    this->m_worker=new Worker();
    this->m_worker->moveToThread(this->m_thread);
    //开始线程
    connect(this,&MainWindow::start_dataPreceed,this->m_worker,&Worker::processedData,Qt::QueuedConnection);
    connect(this->m_worker,&Worker::is_twenty,this,&MainWindow::on_istwenty,Qt::QueuedConnection);
    //线程停止的操作
    connect(this->m_worker,&Worker::tastcancel,this,[this]
            {
        ui->label_2->setText("线程停止");
    });
    //线程结束
    connect(this->m_thread,&QThread::finished,this->m_worker,&QObject::deleteLater);
    // worker销毁后,清空线程指针防止野指针
    connect(m_worker, &QObject::destroyed, this, [this](){
        m_thread->deleteLater();
        m_thread = nullptr;
        m_worker = nullptr;
    });
    this->m_thread->start();
}

MainWindow::~MainWindow()
{
    this->m_thread->requestInterruption();
    this->m_thread->quit();//退出线程的事件循环,不再接收新的信号(但是会把当前函数执行完)
    this->m_thread->wait(1000);//等待线程完成,会阻塞主线程
    delete ui;
}
//线程启动
void MainWindow::on_pushButton_clicked()
{
    QVector<int>vv;
    for(auto i=0;i<10000;i++)
    {
        vv.push_back(i);
    }
    emit start_dataPreceed(vv);
}

void MainWindow::on_istwenty(int temp)
{

    ui->label->setText(QString::number(temp));
}
//终止线程
void MainWindow::on_pushButton_2_clicked()
{
    if(this->m_thread&&this->m_thread->isRunning())
    {
        this->m_thread->requestInterruption();
    }
}

还有一个问题:如果我们用任务队列的话,那ui的刷新频率达不到发送接收信号的更新频率怎么办呢(也就是生产速度大于消费速度)
我们可以用有界队列来解决,比如设定最大边界为5000
如果超过了那就让生产者停止一会,等待消费者消费,或者我们直接把旧数据丢掉
然后我们也可以把数据批量发送给主线程,防止更新一条就发送一条,减少信号与槽的调用
之后我们也可以限制主线程的刷新频率,定时刷新,每100ms刷新一次?
还有就是要减少大数据的拷贝,比如vector容器每个线程都要拷贝一份?不可以,我们最好多线程公用这一个数据,但是加锁

posted on 2026-07-18 22:23  dd_l  阅读(5)  评论(0)    收藏  举报