QT 5.9 最简单的QWaitCondition 多线程同步例子

话不多说,先看运行的结果图:

本程序的例子在下方链接里,请点赞并关注。

程序连接    提取码:t7yh

QWaitCondition 提供如下 一些函数 :

  • wait(QMutex *lockedMutex ), 解锁互斥量 lockedMutex, 并阻塞等待唤醒条件,被唤醒后锁定 lockedM utex 并退出函数 
  • wakeAll(),唤醒所有处于等待状态的线程 , 线程唤醒的顺序不确定,由操作系统的调度策略决定
  • wakeOne(),唤醒一个处于等待状态的线程,唤醒哪个线程不确定,由操作系统的调度策略决定 

      QWaitCondition 一般用于 “ 生产者/消费者 ” ( producer/consumer )模型中 。“ 生产者”产生数据, “ 消费者 ” 使用数据 , 前述的数据来集 、 显示与存储的三线程例子就适用这种模型 。
 

首先我们建立两个类,这两个类在qmythread.h 里面 

#ifndef QMYTHREAD_H
#define QMYTHREAD_H

//#include    <QObject>
#include    <QThread>

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    <QWaitCondition>
#include    <QTime>
#include    <QMutex>


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 循环, 首先需要将互斥量锁定,在执行:

扫描二维码关注公众号,回复: 9813397 查看本文章

       newdataAvailable.wait ( &mutex);

       这条语句以 mutex 作为输入参数 , 内部会首先解锁 mutex , 使其他线程可以使用 mutex ,newdataAvailable 进入等待状态 。 当 QThreadProducer 产生新数据使用 newdataAvailable.wakeAll()唤醒所有线程后 newdataAvailable.wait(&mutex)会再次锁定 mutex , 然后退出阻塞状态 ,以执行后面的语句 。

      下面是页面代码:

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include    <QTimer>

#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 可能还处于条件等待的阻塞状态中,将无法正常结束线程 。

      喜欢这篇文章的同学请点个赞,让我们一起努力。

发布了40 篇原创文章 · 获赞 13 · 访问量 5871

猜你喜欢

转载自blog.csdn.net/weixin_42126427/article/details/104811961