话不多说,先看运行的结果图:
本程序的例子在下方链接里,请点赞并关注。
程序连接 提取码:t7yh
QWaitCondition 提供如下 一些函数 :
QWaitCondition 一般用于 “ 生产者/消费者 ” ( producer/consumer )模型中 。“ 生产者”产生数据, “ 消费者 ” 使用数据 , 前述的数据来集 、 显示与存储的三线程例子就适用这种模型 。
首先我们建立两个类,这两个类在qmythread.h 里面
#ifndef QMYTHREAD_H
#define QMYTHREAD_H
//#include
#include
class QThreadProducer : public QThread
{
Q_OBJECT
private:
bool m_stop=false; //停止线程
protected:
void run() Q_DECL_OVERRIDE;
public:
QThreadProducer();
void stopThread();
};
class QThreadConsumer : public QThread
{
Q_OBJECT
private:
bool m_stop=false; //停止线程
protected:
void run() Q_DECL_OVERRIDE;
public:
QThreadConsumer();
void stopThread();
signals:
void newValue(int seq,int diceValue);
};
#endif // QMYTHREAD_H
对应的cpp
#include "qmythread.h"
#include
#include
#include
static QMutex mutex;
static QWaitCondition newdataAvailable;
static int seq=0;//序号
static int diceValue;
QThreadProducer::QThreadProducer()
{
}
void QThreadProducer::stopThread()
{
QMutexLocker locker(&mutex);
m_stop=true;
}
void QThreadProducer::run()
{
m_stop=false;//启动线程时令m_stop=false
seq=0;
qsrand(QTime::currentTime().msec());//随机数初始化,qsrand是线程安全的
while(!m_stop)//循环主体
{
mutex.lock();
diceValue=qrand(); //获取随机数
diceValue=(diceValue % 6)+1;
seq++;
mutex.unlock();
newdataAvailable.wakeAll();//唤醒所有线程,有新数据了
msleep(500); //线程休眠100ms
}
}
void QThreadConsumer::run()
{
m_stop=false;//启动线程时令m_stop=false
while(!m_stop)//循环主体
{
mutex.lock();
newdataAvailable.wait(&mutex);//会先解锁mutex,使其他线程可以使用mutex
emit newValue(seq,diceValue);
mutex.unlock();
// msleep(100); //线程休眠100ms
}
}
QThreadConsumer::QThreadConsumer()
{
}
void QThreadConsumer::stopThread()
{
QMutexLocker locker(&mutex);
m_stop=true;
}
ps: 如果大家,对QThread, QMutex 不了解的话可以看下相关资料,或者关注我,看我前期写的用法。
QThreadConsumer 用于读取掷骰子的次数和点数,井用 发射信号方式把数据传递出去。
掷骰子的次数和点数的变量定义为共享变量,这样两个线程都可以访问,QThreadProducer::run()函数负责每隔 500 毫秒掷假子产生一次数据,通过使用newdataAvailable.wakeAll () 条件唤醒所有等待的线程,QThreadConsumer :: run () 函数中的 while 循环, 首先需要将互斥量锁定,在执行:
newdataAvailable.wait ( &mutex);
这条语句以 mutex 作为输入参数 , 内部会首先解锁 mutex , 使其他线程可以使用 mutex ,newdataAvailable 进入等待状态 。 当 QThreadProducer 产生新数据使用 newdataAvailable.wakeAll()唤醒所有线程后 newdataAvailable.wait(&mutex)会再次锁定 mutex , 然后退出阻塞状态 ,以执行后面的语句 。
下面是页面代码:
#ifndef DIALOG_H
#define DIALOG_H
#include
#include
#include "qmythread.h"
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
private:
QThreadProducer threadProducer;
QThreadConsumer threadConsumer;
protected:
void closeEvent(QCloseEvent *event);
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private slots:
void onthreadA_started();
void onthreadA_finished();
void onthreadB_started();
void onthreadB_finished();
void onthreadB_newValue(int seq, int diceValue);
void on_btnClear_clicked();
void on_btnStopThread_clicked();
void on_btnStartThread_clicked();
private:
Ui::Dialog *ui;
};
#endif // DIALOG_H
#include "dialog.h"
#include "ui_dialog.h"
void Dialog::closeEvent(QCloseEvent *event)
{//关闭窗口
if (threadProducer.isRunning())
{
threadProducer.stopThread();
threadProducer.wait();
}
if (threadConsumer.isRunning())
{
threadConsumer.terminate(); //因为threadB可能处于等待状态,所以用terminate强制结束
threadConsumer.wait();//
}
event->accept();
}
Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog)
{
ui->setupUi(this);
connect(&threadProducer,SIGNAL(started()),this,SLOT(onthreadA_started()));
connect(&threadProducer,SIGNAL(finished()),this,SLOT(onthreadA_finished()));
connect(&threadConsumer,SIGNAL(started()),this,SLOT(onthreadB_started()));
connect(&threadConsumer,SIGNAL(finished()),this,SLOT(onthreadB_finished()));
connect(&threadConsumer,SIGNAL(newValue(int,int)),this,SLOT(onthreadB_newValue(int,int)));
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::onthreadA_started()
{
ui->LabA->setText("Thread Producer状态: started");
}
void Dialog::onthreadA_finished()
{
ui->LabA->setText("Thread Producer状态: finished");
}
void Dialog::onthreadB_started()
{
ui->LabB->setText("Thread Consumer状态: started");
}
void Dialog::onthreadB_finished()
{
ui->LabB->setText("Thread Consumer状态: finished");
}
void Dialog::onthreadB_newValue(int seq,int diceValue)
{
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);
}
void Dialog::on_btnClear_clicked()
{
ui->plainTextEdit->clear();
}
void Dialog::on_btnStopThread_clicked()
{//结束线程
threadProducer.stopThread();//结束线程的run()函数执行
threadProducer.wait();//
// threadConsumer.stopThread();//结束线程的run()函数执行
threadConsumer.terminate(); //因为threadB可能处于等待状态,所以用terminate强制结束
threadConsumer.wait();//
ui->btnStartThread->setEnabled(true);
ui->btnStopThread->setEnabled(false);
}
void Dialog::on_btnStartThread_clicked()
{//启动线程
threadConsumer.start();
threadProducer.start();
ui->btnStartThread->setEnabled(false);
ui->btnStopThread->setEnabled(true);
}
这里要注意一点:
两个线程启动的先后顺序不应调换,应先启动threadConsumer, 使其先进入 wait 状态 , 后启动 threadProducer , 这样在 threadProducer 里 wakeAll ()时 threadConsumer 就可以及时响应 ,否则会丢失第一次掷假子的数据。
结束线程时,若按照上面的顺序先结束 threadProducer 线程, 则必须使用 terminate()来强制结束 threadConsurner 线程,因为 threadConsumer 可能还处于条件等待的阻塞状态中,将无法正常结束线程 。
喜欢这篇文章的同学请点个赞,让我们一起努力。