最近在阅读Qt 5.9 C++开发指南,为了加深对书本上内容的理解,参照书上的讲解尝试写了一些demo,用于以后工作中查阅,如果涉及侵权请告知,实例程序samp13_2
mythread.h
#ifndef MYTHREAD_H #define MYTHREAD_H #include <QThread> #include <QMutex> #include <QMutexLocker> class MyThread : public QThread { Q_OBJECT private: int _times; int _value; QMutex _mutex; protected: void run(); public: MyThread(); ~MyThread(); bool timesValue(int *times, int *value); }; #endif // MYTHREAD_H
mythread.cpp
#include "mythread.h" #include <QRandomGenerator> MyThread::MyThread() { _times = 0; _value = 0; } MyThread::~MyThread() { } // 现在通过函数的方式与主窗口交互 // 首先要理解线程同步,简单说,就是主线程想要获取子线程的数据,要保证获取到的数据是同步的,说白了,就是主线程第一次获取数据要是子线程第一次产生的数据, // 不能是子线程第二次产生的数据,那这就叫线程同步,实现线程同步方法很多,先这个demo杰少互斥量 // 什么是互斥量,简单说,就是人家在动的东西你不能动,比如人家在上厕所,那么你就不能进去,你进去就凉凉 // 互斥量有几个类可以实现QMutex QMutexLocker,他们的区别就是上完厕所需不需要手动冲厕所 //方法1 //void MyThread::run() //{ // while(1) // { // // 方法1,用QMutex // _mutex.lock(); // 锁上 // _value = QRandomGenerator::global()->generate(); // 用这个类生成随机数 // _value = (_value % 6) + 1; // // 互斥量只能锁一次,如果这里continu就会出错 // // 这个类生成的数可能是负数,如果是负数就从新生成 // if(_value <= 0) // { // _mutex.unlock(); // continue; // } // _times = _times + 1; // _mutex.unlock(); // 打开锁 // msleep(500); // 先睡一会儿在生成数字 // } //} //方法2 void MyThread::run() { while(1) { // 方法2,用QMutexLocker if(1) { // 这个类包裹一下,书上说,会在生命周期外自动析构,操作了下,感觉不一定 // 这个demo用qtimer定时读取数据,很可能线程不同步,通过不停的结束游戏开始游戏,就会出现这种情况,但是不用这个类就不会出现这种情况??? QMutexLocker mutexLocker(&_mutex); _value = QRandomGenerator::global()->generate(); // 用这个类生成随机数 _value = (_value % 6) + 1; // 互斥量只能锁一次,如果这里continu就会出错 // 这个类生成的数可能是负数,如果是负数就从新生成 if(_value <= 0) { continue; } _times = _times + 1; } msleep(500); // 先睡一会儿在生成数字 } } bool MyThread::timesValue(int *times, int *value) { if(_mutex.tryLock()) { *times = _times; *value = _value; _mutex.unlock(); return true; } return false; }
mydialog.h
#ifndef MYDIALOG_H #define MYDIALOG_H #include <mythread.h> #include <QDialog> #include <QPushButton> #include <QPlainTextEdit> #include <QLabel> #include <QTimer> class MyDialog : public QDialog { Q_OBJECT private: QPushButton *_btnStart; QPushButton *_btnFinish; QPushButton *_btnClearInfo; QPlainTextEdit *_plainTextEdit; QLabel *_labStatus; QLabel *_labPicture; MyThread *_thread; QTimer *_timer; int _times; protected: void closeEvent(QCloseEvent *event); public: MyDialog(QWidget *parent = nullptr); ~MyDialog(); void iniUI(); void iniSignalSlots(); private slots: void btnStartClicked(); void btnFinishClicked(); void btnClearInfoClicked(); void timerTimeOut(); }; #endif // MYDIALOG_H
mydialog.cpp
#include "mydialog.h" #include <QHBoxLayout> #include <QVBoxLayout> #include <QGroupBox> #include <QPixmap> #include <QString> MyDialog::MyDialog(QWidget *parent) : QDialog(parent) { iniUI(); // 创建线程 _thread = new MyThread(); // 创建定时器 _timer = new QTimer(); // 初始化次数 _times = 0; iniSignalSlots(); } MyDialog::~MyDialog() { } // 初始化界面 void MyDialog::iniUI() { // 创建按钮 _btnStart = new QPushButton("启动游戏"); _btnFinish = new QPushButton("结束游戏"); _btnFinish->setEnabled(false); _btnClearInfo = new QPushButton("清空文本"); // 布局按钮 QHBoxLayout *layout1 = new QHBoxLayout(); layout1->addWidget(_btnStart); layout1->addWidget(_btnFinish); layout1->addWidget(_btnClearInfo); // 创建文本框和图片显示框 _plainTextEdit = new QPlainTextEdit(); _labPicture = new QLabel(); QPixmap pixmap; pixmap.load(":/images/d0.jpg"); _labPicture->setPixmap(pixmap); // 布局文本框和图片显示框 QHBoxLayout *layout2 = new QHBoxLayout(); layout2->addWidget(_plainTextEdit); layout2->addWidget(_labPicture); // 创建状态栏 _labStatus = new QLabel("游戏状态:未开始"); // 布局状态栏 QHBoxLayout *layout3 = new QHBoxLayout(); layout3->addWidget(_labStatus); // 创建分组框的布局 QVBoxLayout *layout4 = new QVBoxLayout(); layout4->addLayout(layout1); layout4->addLayout(layout2); layout4->addLayout(layout3); // 创建一个分组框 QGroupBox *groupBox = new QGroupBox("掷骰子"); groupBox->setLayout(layout4); // 创建整体布局 QHBoxLayout *layout5 = new QHBoxLayout(); layout5->addWidget(groupBox); // 设置整体布局 setLayout(layout5); resize(800, 600); } // 连接信号与槽 void MyDialog::iniSignalSlots() { connect(_btnStart, SIGNAL(clicked()), this, SLOT(btnStartClicked())); connect(_btnFinish, SIGNAL(clicked()), this, SLOT(btnFinishClicked())); connect(_btnClearInfo, SIGNAL(clicked()), this, SLOT(btnClearInfoClicked())); connect(_timer, SIGNAL(timeout()), this, SLOT(timerTimeOut())); } void MyDialog::btnStartClicked() { _labStatus->setText("游戏状态:进行中"); _thread->start(); _btnStart->setEnabled(false); _btnFinish->setEnabled(true); _timer->start(100); } void MyDialog::btnFinishClicked() { _labStatus->setText("游戏状态:未开始"); _thread->terminate(); _thread->wait(); _btnStart->setEnabled(true); _btnFinish->setEnabled(false); _timer->stop(); } void MyDialog::btnClearInfoClicked() { _plainTextEdit->clear(); } void MyDialog::closeEvent(QCloseEvent *event) { if(_thread->isRunning()) // 如果线程还在继续执行,那么强行终止 { _thread->terminate(); _thread->wait(); } event->accept(); } void MyDialog::timerTimeOut() { int times = 0; int value = 0; bool valid = _thread->timesValue(×, &value); // if(valid && times != _times) // 这里的设置决定线程是否同步,times == _times + 1 if(valid && times == _times + 1) // 这里的设置决定线程是否同步,times == _times + 1,但是万一有一次没接受到,游戏就结束了 { _times = times; QString text = QString::asprintf("第%d次掷骰子,点数为:%d", times, value); _plainTextEdit->appendPlainText(text); QString filename = QString::asprintf(":/images/d%d.jpg", value); QPixmap pixmap; pixmap.load(filename); _labPicture->setPixmap(pixmap); // 给一个label设置图片 } }
main.cpp
#include "mydialog.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MyDialog w; w.show(); return a.exec(); }
效果展示