QThread 创建多线程程序
QThread 类功能简介
今天说一下 Qt 中的多线程。QThread
类不依赖平台的管理线程的方法。一个 QThread
类的对象管理一个线程,一般从 QThread
继承一个自定义类,并重新定义虚函数 run()
,在 run()
函数里实现线程需要完成的任务。
将应用程序的线程称为主线程,额外创建的线程称为工作线程。一般在主线程里创建工作线程,并调用 start()
开始执行工作线程的任务。start()
会在内部调用 run()
函数,进入工作线程的事件循环,在 run()
函数里调用 exit()
或 quit()
可以结束线程的事件循环,或在主线程调用 terminate()
强制结束线程。
QThread
类的主要接口函数、信号和槽函数如下:
类型 | 函数 | 功能 |
---|---|---|
公共函数 | bool isFinished () | 线程是否结束 |
bool isRunning () | 线程是否正在运行 | |
Priority priority () | 返回线程的优先级 | |
公共函数 | void setPriority (Priority priority) | 设置线程优先级 |
void exit (int returnCode = 0) | 退出线程的事件循环,退出代码为 returnCode 0 表示成功,否则表示有错误。 | |
bool wait (unsigned long time) | 阻止线程执行,知道线程结束,或等待时间超过时间 | |
公共槽函数 | void quit () | 退出线程的事件循环,并返回代码 0,等效 exit (0) |
void start (priority priority) | 内部调用 run() 开始执行线程,操作系统根据优先级参数调度 | |
void terminate () | 终止线程运行,但不立刻结束线程,二十等待操作系统结束线程。使用 terminate() 后要 wait() | |
信号 | void finished () | 在线程就要结束的时候发射此信号 |
void started () | 在线程开始执行、run() 函数被调用之前发射此信号 | |
静态公共成员 | int idealThreadCount () | 返回系统上能运行的线程的理想个数 |
void msleep (unsigned long msecs) | 强制当前线程休眠 msecs 毫秒 | |
void sleep (unsigned long secs) | 强制当前线程休眠 secs 秒 | |
void usleep (unsigned long usecs) | 强制当前线程休眠 usecs 微妙 | |
保护函数 | virtual void run () | start() 调用 run() 函数开始线程任务的执行,所以在 run() 函数里实现线程的任务功能 |
int exec () | 由 run() 函数调用,进入线程的事件循环,等待 exit() 退出 |
QThread
时 QObject
的子类,所以可以使用信号与槽机制。QThread
自身定义了 started()
和 finished()
俩个信号,started()
信号在线程开始执行之前发射,也就是在 run()
函数被调用之前,finished()
信号在线程就要结束的时候发射。
投骰子线程 QDiceThread
作为实例,定义一个投骰子的线程类 QDiceThread
,类的声明部分如下:
class QDiceThread : public QThread
{
Q_OBJECT
private:
int m_seq=0;//掷骰子次数序号
int m_diceValue;//骰子点数
bool m_Paused=true; //掷一次骰子
bool m_stop=false; //停止线程
protected:
void run() Q_DECL_OVERRIDE; //线程任务
public:
QDiceThread();
void diceBegin();//掷一次骰子
void dicePause();//暂停
void stopThread(); //结束线程
signals:
void newValue(int seq,int diceValue); //产生新点数的信号
};
- 重载虚函数
run()
,在此函数里完成线程的主要任务。 - 自定义
diceBegin()
、dicePause()
、stopThread()
三个公共函数用于线程控制,这三个函数由主线程调用。 - 定义了要给信号
newValue(int seq, int diceValue)
用于在投一次骰子得到新的点数之后发射此信号,由主线程的槽函数相应以获取值。
QDiceThread
类的实现代码如下:
#include "qdicethread.h"
#include <QTime>
#include <QRandomGenerator>
QDiceThread::QDiceThread()
{
}
void QDiceThread::diceBegin()
{ //开始掷骰子
m_Paused=false;
}
void QDiceThread::dicePause()
{//暂停掷骰子
m_Paused=true;
}
void QDiceThread::stopThread()
{//停止线程
m_stop=true;
}
void QDiceThread::run()
{//线程任务
m_stop=false; //启动线程时令m_stop=false
m_seq=0; //掷骰子次数
// qsrand(QTime::currentTime().msec());//随机数初始化,qsrand是线程安全的,过时的函数
while(!m_stop) //循环主体
{
if (!m_Paused)
{
// m_diceValue=qrand(); //获取随机数,过时的函数
// m_diceValue=(m_diceValue % 6)+1;
m_diceValue= QRandomGenerator::global()->bounded(1,7); //随机数[1,6]
m_seq++;
emit newValue(m_seq,m_diceValue); //发射信号
}
msleep(500); //线程休眠500ms
}
// 在 m_stop==true时结束线程任务
quit();//相当于 exit(0),退出线程的事件循环
}
其中 run() 时线程任务的实现部分,线程开始执行就执行 run() 函数。run 函数一般时事件循环过程,根据各种条件或者事件处理各种任务。当 run 函数退出时,线程的事件循环就结束了。
在 run 函数里,初始化变量 m_stop 和 m_seq,用 qsrand() 函数对随机数种子初始化。run 函数的主循环是个 while 循环,在主线程调用 stopThread 函数使 m_stop 为 true 才会退出 while,调用 quit 之后结束线程。
在 while 循环体内,又根据 m Paused 判断当前是否需要掷散子,如果需要掷骰子,则用随函数生成一次散子的点数 m diceValue,然后发射信号 newValueO),将 mseq 和 m diceValue 作信号参数传递出去。主线程可以设计槽函数与此信号关联,获取这两个值并进行显示。
投骰子多线程应用程序
使用 QDiceThread 类,设计一个应用程序,程序运行界面如图所示:
窗体上方的几个按钮用于控制线程的启动与停止,控制开始与暂停掷骰子。中间的文本框是示次数和点数,右边根据点数显示资源文件里面的一个图片,图片存储在项目的资源文件里。下方的一个标签根据 QDiceThread 的 staredO) 和 GnishedO) 两个信号显示线程的状态。
窗口类是从 QDialog 继承的类 Dialog,其类定义如下 (省略了按钮槽函数的定义):
class Dialog : public QDialog
{
Q_OBJECT
private:
QDiceThread threadA;
protected:
void closeEvent(QCloseEvent *event);
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private slots:
void onthreadA_started();
void onthreadA_finished();
void onthreadA_newValue(int seq, int diceValue);
private:
Ui::Dialog *ui;
};
这里定义了一个 QDiceThread 类型的变量 threadA,重定义了 closeEvent() 事件,自定义了 3 个槽函数。
Dialog 类的构造函数代码如下:
Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog)
{//构造函数
ui->setupUi(this);
connect(&threadA,SIGNAL(started()),this,SLOT(onthreadA_started()));
connect(&threadA,SIGNAL(finished()),this,SLOT(onthreadA_finished()));
connect(&threadA,SIGNAL(newValue(int,int)),this,SLOT(onthreadA_newValue(int,int)));
}
构造函数主要是将 threadA 的 3 个信号与 Dialog 自定义的 3 个槽函数相关联,这 3 个槽的代码如下:
void Dialog::onthreadA_started()
{//线程的started()信号的响应槽函数
ui->LabA->setText("Thread状态:thread started");
}
void Dialog::onthreadA_finished()
{//线程的 finished()信号的响应槽函数
ui->LabA->setText("Thread状态:thread finished");
}
void Dialog::onthreadA_newValue(int seq,int diceValue)
{//QDiceThread的newValue()信号的响应槽函数,显示骰子次数和点数
QString str=QString::asprintf("第 %d 次掷骰子,点数为:%d",seq,diceValue);
ui->plainTextEdit->appendPlainText(str);
QPixmap pic; //图片显示
QString filename=QString::asprintf(":/dice/images/d%d.jpg",diceValue);
pic.load(filename);
ui->LabPic->setPixmap(pic);
}
- started() 信号发射时,表示线程开始执行,在标签里显示状态文字。
- finished() 信号发射时,表示线程结束执行,在标签里显示状态文字。
- newValue() 是 QDiceThread 定义的信号, 在掷一次骰子获得新的点数后发射,将掷骰子的次数和点数传递过来。槽函数 onthreadA_newValue() 获取这两个值并显示在文本框里,再根据点数从资源文件里获取相应的图片并显示。
窗口上五个按钮的代码如下:
void Dialog::on_btnClear_clicked()
{ //清空文本 按钮
ui->plainTextEdit->clear();
}
void Dialog::on_btnDiceEnd_clicked()
{//暂停 掷骰子按钮
threadA.dicePause();
ui->btnDiceBegin->setEnabled(true);
ui->btnDiceEnd->setEnabled(false);
}
void Dialog::on_btnDiceBegin_clicked()
{//开始 掷骰子按钮
threadA.diceBegin();
ui->btnDiceBegin->setEnabled(false);
ui->btnDiceEnd->setEnabled(true);
}
void Dialog::on_btnStopThread_clicked()
{//结束线程 按钮
threadA.stopThread();//结束线程的run()函数执行
threadA.wait();//
ui->btnStartThread->setEnabled(true);
ui->btnStopThread->setEnabled(false);
ui->btnDiceBegin->setEnabled(false);
ui->btnDiceEnd->setEnabled(false);
}
void Dialog::on_btnStartThread_clicked()
{//启动线程 按钮
threadA.start();
ui->btnStartThread->setEnabled(false);
ui->btnStopThread->setEnabled(true);
ui->btnDiceBegin->setEnabled(true);
ui->btnDiceEnd->setEnabled(false);
}
- “启动线程”按钮调用线程的 start () 函数,star 0 的数会内部调用 un 0 函数开始线程任务的执行。runO 函数将内部变量 m_Paused 初始化为 tue,所以,启动线程后并不会立即开始掷骰子。“开始”按钮调用 diceBegin () 函数,使 threadA 线程内部变量 mPaused 变为 false,那么 runO 函数里就开始每隔 500 毫秒产生一次骰子点数,并发射信号 newValueO。
- “暂停”按钮调用 dicePause () 函数,使 threadA 线程内部变量 mPaused 变为 true,runO) 函数里不再掷骰子,但是 runO 函数并没有结束,也就是线程并没有结束。
- “结束线程”按钮调用 stopThread () 函数,使 threadA 线程内部的 mstop 变为 true,runO) 函数体的 while 循环结束,执行 quit 0 后线程结束。所以,线程结束就是 runO) 函数执行退出。
重载 closeEvent () 事件,在窗口关闭时确保线程被停止,代码如下:
void Dialog::closeEvent(QCloseEvent *event)
{ //窗口关闭事件,必须结束线程
if (threadA.isRunning())
{
threadA.stopThread();
threadA.wait();
}
event->accept();
}
资料参考:https://github.com/0voice
标签:run,Qt,void,线程,Dialog,ui,多线程,QThread,函数 From: https://blog.csdn.net/H520xcodenodev/article/details/143528520