从零开始学Qt(77):多线程程序实例 - 投骰子

从零开始学Qt(77):多线程程序实例 - 投骰子

首页动作格斗多线程游戏更新时间:2024-06-08

作为实例,定义一个掷骰子的线程类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