C++Qt开发-Windows下QThread多线程与自定义信号槽

本文介绍了在Windows的Qt环境中使用QThread进行多线程编程的方法,包括自定义线程类,重写run函数,以及使用QMutex互斥锁保证线程安全。同时,讲解了如何通过信号槽机制实现子线程与主线程间的通信,避免直接操作UI,确保程序的正确性和响应性。示例代码展示了如何创建线程池,处理任务并更新UI,以及正确结束线程。
摘要由CSDN通过智能技术生成

QThread

在Windows的Qt中想要实现多线程编程,最简单的方式是继承QThread类,自己定义线程类,重写run函数,实现线程功能。

static QMutex t_mutex;//互斥锁
class MyThread : public QThread
{
    //Q_OBJECT
    //这个宏定义可以实现线程的信号机制
public:
    queue<long long> *array;
    vector<long long> value_vector;
    void run()
    {
        qDebug() << objectName() << "run!" << endl;

        //锁

        while( array->empty() != true )
        {

            long long value;

            t_mutex.lock();//加锁
            value = array->front();
            qDebug() << "value:" << value << endl;
            array->pop();
            t_mutex.unlock();//解锁

            int k = (int)sqrt(value);
            //qDebug() << k << endl;
            int i;
            for(i = 2; i <= k; ++i)
            {
                if( value%i == 0)
                {
                  break;
                }
            }
            if( i<k )
            {
                qDebug() << value << "不是素数" << endl;
            }
            else
            {
                //QString string = QString::number(value) + " 是素数";
                value_vector.push_back(value);
                //ui->listWidget_zhishu->addItem(string);
                qDebug() << value << "是素数" << endl;
            }
        }
    }
    //引用传递,减少拷贝的损耗
    MyThread(queue<long long> &array)
    {
        this->array = &array;
    }

signals:
    //自定义信号,完成子线程对ui的操作
    void SendUI(QString &string);
};
    QTime time_begin = QTime::currentTime();
    vector<MyThread*> thread;//模拟线程池
    for(int i = 1; i <= THREAD_NUM; i++)
    {
        MyThread *mythread = new MyThread(array);//引用传递,减少拷贝的损耗

        mythread->setObjectName("thread" + QString::number(i));//设置线程名字
        mythread->start();
        thread.push_back(mythread);
    }

    QMessageBox::warning(this,"warning","线程运行中!");

    for(int i = 0; i <= THREAD_NUM-1; i++)
    {
        qDebug() << "wait 次数:" << i << endl;
        thread.at(i)->wait();//在wait一次就会堵塞
    }

    while(thread.empty() != true)
    {
        if(thread.back()->value_vector.empty() != true)
        {
            QString string = QString::number(thread.back()->value_vector.back()) + " 是素数";
            ui->listWidget_zhishu->addItem(string);
            thread.back()->value_vector.pop_back();
        }
        thread.back()->terminate();//终止进程
        free(thread.back());//释放堆内存
        thread.pop_back();//弹出栈
    }
    QTime time_end = QTime::currentTime();

    QString string = "本次花费时间:" + time_begin.toString() + "——" + time_end.toString();
    ui->listWidget_zhishu->addItem(string);

在这里插入图片描述
QThread创建的线程,如果在主线程中不wait子线程,那么子线程和主线程将各自运行,主线程不会堵塞,那么相应的,主线程可能不能按时得到子线程的结果反馈,所以必须要等待子线程的执行完毕,才能继续主线程,那么主线程就会处于阻塞状态,运行时操作界面无法操作。

既然子线程中不能操作ui,那么子线程中所有直接或间接的ui操作都是违法的。

解决的方法是通过信号槽机制,子线程是无法直接使用ui的,ui只有主线程能使用,那么只能通过子线程发送一个信号给主线程,主线程接受信号做出改变ui的操作,这样子线程的结果可以得到反馈,主线程也不会阻塞。

自定义信号槽

mythread.h

class MyThread : public QThread
{
    Q_OBJECT
public:
    explicit MyThread(QObject *parent = nullptr);
    void run() override;

signals:
    void SendUI(const QString &msg);
};

mythread.cpp

#include "mythread.h"

MyThread::MyThread(QObject *parent) : QThread(parent)
{

}
void MyThread::run()
{
    int num = 0;
    while(num < 999999)
    {
        if(num == 888888)
        {
            QString msg = QString::number(num);
            emit SendUI(msg);
        }
        qDebug() << num << endl;
        num++;
    }
}

mainwidget.h

class MyWidget : public QWidget
{
    Q_OBJECT

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

private:
    Ui::MyWidget *ui;
private slots:
    void SLOT_main(QString msg);
};

mainwidget.cpp

#include "mywidget.h"
#include "ui_mywidget.h"
#include "mythread.h"
MyWidget::MyWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::MyWidget)
{
    ui->setupUi(this);
    MyThread *mythread = new MyThread(this);

    mythread->start();
    connect(mythread, SIGNAL(SendUI(const QString &)), this, SLOT(SLOT_main(const QString &)));
}

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

void MyWidget::SLOT_main(const QString msg)
{
    ui->listWidget->addItem("信号槽启动");
    ui->listWidget->addItem(msg);
}

注意,启动线程应是 thread->start(),而不是thread->run()
thread->start() 才是启动线程,此时主线程和子线程同时存在,子线程跑run函数
thread->run() 只是在主线程中调用子线程类的run方法,没有生成线程。

既然非阻塞,主线程中就不要再调用 thread->wait() 函数。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橙子砰砰枪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值