点击上方“Qt学视觉”,选择“星标”公众号重磅干货,第一时间送达
想要学习的同学们还请认真阅读每篇文章,相信你一定会有所收获
线程同步的概念
在多线程应用程序中,由于多个线程的存在,线程之间可能需要访问同一个变量或一个线程需要等待另外一个线程完成某个操作后才产生相应的动作。
基于互斥量的线程同步
QMutex和QMutexLocker是基于互斥量的线程同步类,QMutex定义的实例是一个互斥量,QMutex只要提供3个函数:
lock():锁定互斥量,如果另外一个线程锁定了这个互斥量,它将阻塞执行直到其他线程解锁这个互斥量
unlock():解锁一个互斥量,需要与lock()配对使用
tryLock():试图锁定一个互斥量,如果成功锁定就返回true,如果其他线程已经锁定了这个互斥量,就返回false,但不阻塞程序执行
使用互斥量,对QDiceThread类重新定义,不采用信号与槽机制,而是提供一个函数用于主线程读取数据,更改后的QDiceThread类定义如下:
头文件
#pragma once#include #include class QDiceThread : public QThread{ Q_OBJECTprivate: QMutex m_mutex; //互斥量 int m_nSeq; //掷骰子次数序号 int m_nDiceValue; //骰子点数 bool m_bPaused; //暂停 bool m_bStop; //停止protected: void run() Q_DECL_OVERRIDE;//线程任务public: QDiceThread(); ~QDiceThread(); void diceBegin();//掷一次骰子 void dicePause();//暂停 void stopThread();//结束线程 bool readValue(int *seq, int *diceValue);//用于主线程读取数据的函数}
源文件
#include "QDiceThread.h"#include QDiceThread::QDiceThread(){ m_nSeq = 0; m_nDiceValue = 0; m_bPaused = true; m_bStop = false;}QDiceThread::~QDiceThread(){}//线程任务void QDiceThread::run(){ m_bStop = false;//启动线程时将m_bStop->false m_nSeq = 0;//掷骰子次数 qsrand(QTime::currentTime().msec());//随机数初始化,qsrand是线程安全的 while (!m_bStop)//循环主体 { if (!m_bPaused) { //QMutexLocker Locker(&m_mutex); m_mutex.lock(); m_nDiceValue = qrand();//获取随机数 m_nDiceValue = (m_nDiceValue % 6) + 1; m_nSeq++; m_mutex.unlock(); } msleep(500);//线程休眠500ms }}//掷一次骰子void QDiceThread::diceBegin(){ m_bPaused = false;}//暂停void QDiceThread::dicePause(){ m_bPaused = true;}//结束线程void QDiceThread::stopThread(){ m_bStop = true;}//用于主线程读取数据的函数bool QDiceThread::readValue(int* seq, int* diceValue){ if (m_mutex.tryLock()) { *seq = m_nSeq; *diceValue = m_nDiceValue; m_mutex.unlock(); return true; } return false;}
主界面主要就是增加了一个定时器,用来读取线程里的数值
头文件
#pragma once#include #include "ui_QGuiThread.h"#include "QDiceThread.h"#include class QGuiThread : public QWidget{ Q_OBJECTprivate: QDiceThread threadA; int m_nSeq, m_nDiceValue; QTimer m_timer;//定时器protected: void closeEvent(QCloseEvent* event);public: QGuiThread(QWidget *parent = Q_NULLPTR); ~QGuiThread();private slots: void onthreadA_started(); void onthreadA_finished(); void onTimeOut(); //定期器处理槽函数 void btnStartThread_clicked(); void btnStopThread_clicked(); void btnDiceBegin_clicked(); void btnDiceEnd_clicked(); void btnClear_clicked();private: Ui::QGuiThread ui;};
源文件
#include "QGuiThread.h"#pragma execution_character_set("utf-8")void QGuiThread::closeEvent(QCloseEvent* event){ //窗口关闭事件,必须结束线程 if (threadA.isRunning()) { threadA.stopThread(); threadA.wait(); } event->accept();}QGuiThread::QGuiThread(QWidget *parent) : QWidget(parent){ ui.setupUi(this); connect(&threadA, SIGNAL(started()), this, SLOT(onthreadA_started())); connect(&threadA, SIGNAL(finished()), this, SLOT(onthreadA_finished())); connect(&m_timer, SIGNAL(timeout()), this, SLOT(onTimeOut())); connect(ui.btnStartThread, SIGNAL(clicked()), this, SLOT(btnStartThread_clicked())); connect(ui.btnStopThread, SIGNAL(clicked()), this, SLOT(btnStopThread_clicked())); connect(ui.btnDiceBegin, SIGNAL(clicked()), this, SLOT(btnDiceBegin_clicked())); connect(ui.btnDiceEnd, SIGNAL(clicked()), this, SLOT(btnDiceEnd_clicked())); connect(ui.btnClear, SIGNAL(clicked()), this, SLOT(btnClear_clicked()));}QGuiThread::~QGuiThread(){ if (threadA.isRunning()) { threadA.stopThread(); threadA.wait(); }}void QGuiThread::onthreadA_started(){ ui.LabA->setText("Thread状态:thread started");}void QGuiThread::onthreadA_finished(){ ui.LabA->setText("Thread状态:thread finished");}void QGuiThread::onTimeOut(){ int tmpSeq = 0, tmpValue = 0; bool valid = threadA.readValue(&tmpSeq, &tmpValue); //读取数值 if (valid && (tmpSeq != m_nSeq)) //有效,并且是新数据 { m_nSeq = tmpSeq; m_nDiceValue = tmpValue; QString str = QString::asprintf("第 %d 次掷骰子,点数为:%d", m_nSeq, m_nDiceValue); ui.plainTextEdit->appendPlainText(str); QPixmap pic; QString filename = QString::asprintf(":/QGuiTest/icons/d%d.jpg", m_nDiceValue); pic.load(filename); ui.LabPic->setPixmap(pic); }}void QGuiThread::btnStartThread_clicked(){ m_nSeq = 0; threadA.start(); ui.btnStartThread->setEnabled(false); ui.btnStopThread->setEnabled(true); ui.btnDiceBegin->setEnabled(true); ui.btnDiceEnd->setEnabled(false);}void QGuiThread::btnStopThread_clicked(){ threadA.stopThread();//结束线程的run()函数执行 threadA.wait();// ui.btnStartThread->setEnabled(true); ui.btnStopThread->setEnabled(false); ui.btnDiceBegin->setEnabled(false); ui.btnDiceEnd->setEnabled(false);}void QGuiThread::btnDiceBegin_clicked(){ threadA.diceBegin(); m_timer.start(100); //定时器100读取一次数据 ui.btnDiceBegin->setEnabled(false); ui.btnDiceEnd->setEnabled(true);}void QGuiThread::btnDiceEnd_clicked(){ threadA.dicePause(); m_timer.stop();//定时器暂停 ui.btnDiceBegin->setEnabled(true); ui.btnDiceEnd->setEnabled(false);}void QGuiThread::btnClear_clicked(){ ui.plainTextEdit->clear();}
界面效果