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() 函数。