作为实例,定义一个掷骰子的线程类QDiceThread,然后在主线程中创建和控制子线程的运行。通过这个实例,可以了解多线程程序的基本原理。
QDiceThread的定义首先新建一个Widget项目。然后依次点击“New File or Project” – “C ” – “C Class”新建类,名称设置为QDiceThread,基类设置为QThread,勾选“Add Q_OBJECT”。
类的声明部分如下:
#include <QThread>
class QDiceThread : public QThread
{
Q_OBJECT
public:
QDiceThread();
void diceBegin(); //掷一次骰子
void dicePause(); //暂停
void stopThread(); //结束线程
protected:
void run() override; //线程任务
private:
int m_seq=0; //掷骰子次数序号
int m_diceValue; //骰子点数
bool m_Paused=true; //暂停
bool m_stop=false; //停止
SIGNALs:
void newValue(int seq,int diceValue); //产生新点数的信号
};
重载虚函数run(),在此函数里完成线程的主要任务。
自定义diceBegin()、dicePause()、stopThread()3个公共函数用于线程控制,这3个函数由主线程调用。
定义了一个信号newValue(int seq,int diceValue)用于在掷一次骰子得到新的点数之后发射此 信号,由主线程的槽函数响应以获取值。
QDiceThread类的实现QDiceThread类的实现代码如下:
#include "qdicethread.h"
#include <QTime>
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_seq ;
emit newValue(m_seq,m_diceValue);//发射信号
}
msleep(500); //线程休眠 500ms
}
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,然后发射信号newValue(),将m_seq和m_diceValue作为信号参数传递出去。主线程可以设计槽函数与此信号关联,获取这两个值并进行显示。
QDiceThread类的使用使用QDiceThread类,设计一个应用程序,程序运行界面如图所示。
窗体上方的几个按钮用于控制线程的启动与停止,控制开始与暂停掷骰子。中间的文本框 显示次数和点数,右边根据点数显示资源文件里面的一个图片,图片存储在项目的资源文件里。
下方的一个标签根据QDiceThread的started()和 finished()两个信号显示线程的状态。
窗口类是从QWidget继承的类Widget,其类定义如下(省略了按钮槽函数的定义):
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
protected:
void closeEvent(QCloseEvent *event);
private:
Ui::Widget *ui;
QDiceThread threadA;
private slots:
//自定义槽函数
void on_threadA_started();
void on_threadA_finished();
void on_threadA_newValue(int seq, int diceValue);
};
这里定义了一个QDiceThread类型的变量threadA,重定义了closeEvent()事件,自定义了 3个槽函数。
Widget类的构造函数代码如下:
Widget::Widget(QWidget *parent)
: QWidget(parent), ui(new Ui::Widget)
{//构造函数
ui->setupUi(this);
connect(&threadA,SIGNAL(started()),
this,SLOT(on_threadA_started()));
connect(&threadA,SIGNAL(finished()),
this,SLOT(on_threadA_finished()));
connect(&threadA,SIGNAL(newValue(int,int)),
this,SLOT(on_threadA_newValue(int,int)));
}
构造函数主要是将threadA的3个信号与Widget自定义的3个槽函数相关联,这3个槽函数的代码如下:
void Widget::on_threadA_started()
{//线程的started()信号的响应槽函数
ui->labStat->setText ("Thread 状态:thread started");
}
void Widget::on_threadA_finished()
{//线程的finished()信号的响应槽函数
ui->labStat->setText ("Thread 状态:thread finished");
}
void Widget::on_threadA_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定义的信号,在掷一次骰子获得新的点数后发射,将掷骰子的次数和点数传递过来。槽函数on_threadA_newValue()获取这两个值并显示在文本框里,再根据点数从资源文件里获取相应的图片并显示。
窗口上5个按钮的代码如下:
void Widget::on_btnStartThread_clicked()
{//启动线程按钮
threadA.start();
ui->btnStartThread->setEnabled(false);
ui->btnStopThread->setEnabled(true);
ui->btnStartDice->setEnabled(true);
ui->btnStopDice->setEnabled(false);
}
void Widget::on_btnStopThread_clicked()
{//结束线程按钮
threadA.stopThread(); //结束线程的 run ()函数执行
threadA.wait();
ui->btnStartThread->setEnabled(true);
ui->btnStopThread->setEnabled(false);
ui->btnStartDice->setEnabled(false);
ui->btnStopDice->setEnabled(false);
}
void Widget::on_btnStartDice_clicked()
{//开始掷骰子按钮
threadA.diceBegin();
ui->btnStartDice->setEnabled(false);
ui->btnStopDice->setEnabled(true);
}
void Widget::on_btnStopDice_clicked()
{//暂停掷骰子按钮
threadA.dicePause();
ui->btnStartDice->setEnabled(true);
ui->btnStopDice->setEnabled(false);
}
void Widget::on_btnClear_clicked()
{//清空文本按钮
ui->plainTextEdit->clear();
}
“启动线程”按钮调用线程的start()函数,start()函数会内部调用run()函数开始线程任务的执行。run()函数将内部变量m_Paused初始化为true,所以,启动线程后并不会立即开始掷骰子。
“开始”按钮调用diceBegin()函数,使threadA线程内部变量m_Paused变为false,那么run() 函数里就开始每隔500毫秒产生一次骰子点数,并发射信号newValue()。
“暂停”按钮调用dicePause()函数,使threadA线程内部变量m_Paused变为true,run()函数里不再掷骰子,但是run()函数并没有结束,也就是线程并没有结束。
“结束线程”按钮调用stopThread()函数,使threadA线程内部的m_stop变为true,run()函数 体的while循环结束,执行quit()后线程结束。所以,线程结束就是run()函数执行退出。
重载closeEvent()事件,在窗口关闭时确保线程被停止,代码如下:
void Widget::closeEvent(QCloseEvent *event)
{//窗口关闭事件,必须结束线程
if(threadA.isRunning()){
threadA.stopThread();
threadA.wait();
}
event->accept();
}
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved