QT--线程阻塞问题--开启多线程(帮助你快速解决因大量计算耗时而导致主线程渲染阻塞的问题、简单实现多线程教学)

目录

        1、问题简介

         2、解决方案

        3、解决示例


1、问题简介

        在实际开发中,我们经常会进行一些比较复杂的计算和算法实现,或者是在某些特定的情况下会实例化一些类。这些操作都是很消耗时间的,如果在此时需要进行一些UI的渲染的话,这些耗时操作就会阻塞渲染线程,导致无法达到想要的效果。

        例如,我们想要在进行计算时,显示一个等待动画界面,直到计算结果出来后再关闭掉这个等待界面。但是这就会导致等待界面渲染阻塞,而当计算结果出来后又将界面关闭,带来的用户使用效果就是好像卡住了一样。

        问题代码示例:

        (1).h文件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QMovie>
#include <QList>
#include <QRandomGenerator>
#include <QDateTime>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    void comIntensive();

protected:
    void mousePressEvent(QMouseEvent *e) override;

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

        (2).cpp文件

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QMovie *gif = new QMovie();
    gif->setFileName(":/gif.gif");
    ui->label->setMovie(gif);
    // 设置QLabel的背景为透明
    ui->label->setStyleSheet("background-color: transparent;");
    gif->start();
    ui->label->hide();
}


MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::comIntensive()
{
    QList<double> res;
    do{
        double resInf = ((qrand() * 1.00)/(RAND_MAX * 10)) * 1000.00;
        if(res.isEmpty()){
            res.append(resInf);
        }
        else{
            if(!res.contains(resInf)){
               res.append(resInf);
            }
        }
    }
    while(!res.isEmpty() && res.size() <= 10000);
    foreach(auto var, res) {
        ui->plainTextEdit->appendHtml(QString::number(var,'f',2));
    }

}

void MainWindow::mousePressEvent(QMouseEvent *e)
{
    if(e->button() == Qt::LeftButton){
        ui->plainTextEdit->clear();
        ui->label->show();
        comIntensive();
        ui->label->hide();
    }
}

        在以上示例中,我希望生成10000个(0,100)之间的不重复的含有两位小数的double类型的浮点数,在生成期间我采用QMovie来显示一个gif动画,这样在生成期间就可以有一个等待动画界面。

        但是在实际操作时,由于线程阻塞而导致没有达到想要的效果,反而给用户一种卡顿的感觉。

 2、解决方案

        针对以上问题,有两个方法可以解决:

        ①开启子线程,将QMovie动画交给子线程来处理,而主线程则主要负责进行数据的计算和产生,这样的话就能够解决线程阻塞的问题。但是这样做也有一些问题,一般而言,在qt中我们会将有关UI渲染的东西都放置在主线程内,而不是通过子线程去实现。

        ②开启子线程,将复杂计算内容交给子线程来处理,主线程主要进行UI的渲染操作,这样同样能够解决线程阻塞的问题,同时这样也更符合我们的生产消费设计模式。

3、解决示例

        (1).h文件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QMovie>
#include <QList>
#include <QRandomGenerator>
#include <QDateTime>
#include <QDebug>
#include <QTimer>

#include "threadlock.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

//    void comIntensive();

    ThreadLock *mythread;
    QMovie *gif;

protected:
    void mousePressEvent(QMouseEvent *e) override;

private:
    Ui::MainWindow *ui;

public slots:
    void onThreadFinished();
};
#endif // MAINWINDOW_H
#ifndef THREADLOCK_H
#define THREADLOCK_H

#include <QThread>
#include <QDebug>
#include <QReadWriteLock>

enum SM_Thread{
    TYPE_INFO,
    TYPE_DEG,
    TYPE_RES,
    TYPR_WAR
};
Q_NAMESPACE
Q_ENUM_NS(SM_Thread)

class ThreadLock : public QThread
{
    Q_OBJECT
public:
    ThreadLock();
    ~ThreadLock();

    QList<QString> res;
    SM_Thread from = TYPR_WAR;
    void setLogic(SM_Thread var);

    void comIntensive();
protected:
    void run() override;

private:
    QReadWriteLock Lock;

};

#endif // THREADLOCK_H

        (2).cpp文件

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    gif = new QMovie();
    gif->setFileName(":/gif.gif");
    ui->label->setMovie(gif);
    // 设置QLabel的背景为透明
    ui->label->setStyleSheet("background-color: transparent;");
    gif->start();
    ui->label->hide();
}


MainWindow::~MainWindow()
{
    delete ui;
}

//void MainWindow::comIntensive()
//{
//    QList<double> res;
//    do{
//        double resInf = ((qrand() * 1.00)/(RAND_MAX * 10)) * 1000.00;
//        if(res.isEmpty()){
//            res.append(resInf);
//        }
//        else{
//            if(!res.contains(resInf)){
//               res.append(resInf);
//            }
//        }
//    }
//    while(!res.isEmpty() && res.size() <= 10000);
//    foreach(auto var, res) {
//        ui->plainTextEdit->appendHtml(QString::number(var,'f',2));
//    }
//}

void MainWindow::mousePressEvent(QMouseEvent *e)
{
    if(e->button() == Qt::LeftButton){
        ui->plainTextEdit->clear();
        ui->label->show();
        mythread = new ThreadLock();
        connect(mythread, &ThreadLock::finished, this, &MainWindow::onThreadFinished);
        mythread->setLogic(SM_Thread::TYPR_WAR);
        mythread->start(QThread::LowPriority);
        gif->start();
    }
    else{
    }
}

void MainWindow::onThreadFinished()
{
    SM_Thread type = mythread->from;
    switch (type) {
    case SM_Thread::TYPE_DEG:
        break;
    case SM_Thread::TYPE_RES:
        break;
    case SM_Thread::TYPE_INFO:
        break;
    case SM_Thread::TYPR_WAR:
        QList<QString> res = mythread->res;
        foreach(auto var, res) {
            ui->plainTextEdit->appendPlainText(var);
            //让主程序继续执行代码
            QCoreApplication::processEvents();
        }
        break;
    }
    ui->label->hide();
    mythread->quit();
    mythread->deleteLater();
    mythread->wait();
}


#include "threadlock.h"

ThreadLock::ThreadLock()
{

}

ThreadLock::~ThreadLock()
{

}

void ThreadLock::setLogic(SM_Thread var)
{
    from = var;
}

void ThreadLock::run()
{
    switch(from){
    case TYPE_INFO :
        //执行对应代码
        //test();
        break;
    case TYPE_DEG :
        //执行对应代码
        //test();
        break;
    case TYPE_RES :
        //执行对应代码
        //test();
        break;
    case TYPR_WAR :
        //执行对应代码
        comIntensive();
        break;
    }

}

void ThreadLock::comIntensive()
{
    res.clear();
    do{
        double resInf = ((qrand() * 1.00)/(RAND_MAX * 10)) * 1000.00;
        QString resStr = QString::number(resInf,'f',2);
        if(!res.contains(resStr)){
            Lock.tryLockForWrite();
            res.append(resStr);
            Lock.unlock();
        }
        else{

        }
    }
    while(!res.isEmpty() && res.size() <= 10000);
}

        (3)实现效果:

        该解决方法的实现思路主要是通过将复杂计算移植子线程进行运算的方法,从而避免了主线程渲染阻塞的问题。

        在实际操作中我遇到了一个比较有意思的小问题,当接受到产生的数据时,我们需要将其渲染到UI界面上时,由于数据量过于庞大导致渲染速度慢,同样也会导致其他UI渲染阻塞。

        针对这个问题,我采取了如下方法来保证其他的UI线程不卡顿,从而达到想要的效果。

QCoreApplication::processEvents();

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt中,可以使用信号和槽机制来实现多线程多路视频数据传输到线程。具体实现步骤如下: 1. 在每个视频数据的处理线程中定义一个信号,用于向线程发送数据。 2. 在线程中定义一个槽函数,用于接收视频数据。 3. 在每个视频数据的处理线程中,当有数据需要传输到线程时,通过发射信号的方式将数据发送给线程。 4. 在线程中,将每个视频数据处理线程的信号连接到槽函数上,当信号发射时,槽函数将被调用,从而实现多路视频数据传输到线程的目的。 示例代码如下: 在每个视频数据处理线程中定义信号: ``` signals: void videoDataReady(QByteArray data); ``` 在线程中定义槽函数: ``` private slots: void onVideoDataReady(QByteArray data); ``` 在每个视频数据处理线程中,当有数据需要传输到线程时,通过发射信号的方式将数据发送给线程: ``` QByteArray videoData; // 处理后的视频数据 emit videoDataReady(videoData); ``` 在线程中,将每个视频数据处理线程的信号连接到槽函数上: ``` connect(videoThread1, &VideoThread::videoDataReady, this, &MainWindow::onVideoDataReady); connect(videoThread2, &VideoThread::videoDataReady, this, &MainWindow::onVideoDataReady); // ... ``` 当信号发射时,槽函数将被调用,从而实现多路视频数据传输到线程的目的: ``` void MainWindow::onVideoDataReady(QByteArray data) { // 处理接收到的视频数据 } ``` 在多线程编程中,还需要注意线程安全性问题,避免出现数据竞争等问题。为了保证线程安全,可以使用Qt提供的线程同步机制,例如互斥锁、读写锁等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值