The simplest QWaitCondition multithreaded synchronization example for QT 5.9

Say nothing, but first look at the results of the operation:

 

Examples of this program are listed in the links below, which should be appreciated and followed.

Program Connection Extraction Code: t7yh

QWaitCondition provides the following functions:

  • Wait (QMutex *lockedM utex), unlocks the mutex lockedM utex, blocks the wait wake condition, lockedM utex is locked after waking up and exits the function
  • wakeAll(), wakes all waiting threads in an indeterminate order, determined by the scheduling policy of the operating system
  • wakeOne(), wakes a thread in a waiting state, which thread is not sure which to wake, depending on the scheduling policy of the operating system

QWaitCondition s are generally used in producer/consumer models.Producer produces data, Consumer uses data, and the three-threaded example of data collection, display, and storage described above applies to this model.
 

First, let's build two classes, which are in qmythread.h

#ifndef QMYTHREAD_H
#define QMYTHREAD_H

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

class QThreadProducer : public QThread
{
    Q_OBJECT
private:
    bool    m_stop=false; //Stop Thread
protected:
    void    run() Q_DECL_OVERRIDE;
public:
    QThreadProducer();
    void    stopThread();
};


class QThreadConsumer : public QThread
{
    Q_OBJECT
private:
    bool    m_stop=false; //Stop Thread
protected:
    void    run() Q_DECL_OVERRIDE;
public:
    QThreadConsumer();
    void    stopThread();
signals:
    void    newValue(int seq,int diceValue);
};
#endif // QMYTHREAD_H

Corresponding cpp

#include    "qmythread.h"
#include    <QWaitCondition>
#include    <QTime>
#include    <QMutex>


static QMutex  mutex;
static QWaitCondition  newdataAvailable;

static int  seq=0;//Sequence Number
static int  diceValue;

QThreadProducer::QThreadProducer()
{

}

void QThreadProducer::stopThread()
{
    QMutexLocker  locker(&mutex);
    m_stop=true;
}

void QThreadProducer::run()
{
    m_stop=false;//Thread start time m_stop=false
    seq=0;
    qsrand(QTime::currentTime().msec());//Random number initialization, qsrand is thread safe

    while(!m_stop)//Cycle body
    {
        mutex.lock();
        diceValue=qrand(); //Get Random Numbers
        diceValue=(diceValue % 6)+1;
        seq++;
        mutex.unlock();

        newdataAvailable.wakeAll();//Wake up all threads with new data
        msleep(500); //Thread sleeps 100ms
    }
}


void QThreadConsumer::run()
{
    m_stop=false;//Thread start time m_stop=false
    while(!m_stop)//Cycle body
    {
        mutex.lock();
        newdataAvailable.wait(&mutex);//mutex is unlocked so that other threads can use it
        emit    newValue(seq,diceValue);
        mutex.unlock();
//        msleep(100); //Thread sleeps 100ms
    }

}

QThreadConsumer::QThreadConsumer()
{

}

void QThreadConsumer::stopThread()
{
    QMutexLocker  locker(&mutex);
    m_stop=true;
}

ps: If you don't know QThread, QMutex, you can look at the relevant information, or pay attention to me, see the usage I wrote earlier.

QThreadConsumer reads the number of dice rolls and the number of points, and wells transmit the data by sending a signal.
Variables for the number of dice rolls and the number of points are defined as shared variables so that both threads can access, and the QThreadProducer::run() function is responsible for generating data every 500 milliseconds by throwing a dummy. By waking up all waiting threads using the newdataAvailable.wakeAll() condition, the QThreadConsumer::while loop in the run() function,First you need to lock the mutex to execute:

       newdataAvailable.wait ( &mutex);

This statement takes mutex as an input parameter and unlocks mutex internally so that other threads can use mutex, newdataAvailable, to a wait state.When the QThreadProducer generates new data and wakes all threads with newdataAvailable.wakeAll(), newdataAvailable.wait (&mutex) locks mutex again and exits the blocking state to execute the following statement.

Following is the page code:

#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)
{//close window
    if (threadProducer.isRunning())
    {
        threadProducer.stopThread();
        threadProducer.wait();
    }

    if (threadConsumer.isRunning())
    {
        threadConsumer.terminate(); //Force end with terminate because threadB may be in a waiting state
        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 state: started");
}

void Dialog::onthreadA_finished()
{
    ui->LabA->setText("Thread Producer state: finished");
}

void Dialog::onthreadB_started()
{
    ui->LabB->setText("Thread Consumer state: started");
}

void Dialog::onthreadB_finished()
{
    ui->LabB->setText("Thread Consumer state: finished");
}

void Dialog::onthreadB_newValue(int seq,int diceValue)
{
    QString  str=QString::asprintf("No. %d Dice roll twice with:%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()
{//End Thread
    threadProducer.stopThread();//End thread run() function execution
    threadProducer.wait();//

//    threadConsumer.stopThread();//end thread run() function execution
    threadConsumer.terminate(); //Force end with terminate because threadB may be in a waiting state
    threadConsumer.wait();//

    ui->btnStartThread->setEnabled(true);
    ui->btnStopThread->setEnabled(false);
}

void Dialog::on_btnStartThread_clicked()
{//Start Thread
    threadConsumer.start();
    threadProducer.start();

    ui->btnStartThread->setEnabled(false);
    ui->btnStopThread->setEnabled(true);
}

One thing to note here is:

The order in which the two threads are started should not be swapped. Start threadConsumer first to make it wait, then start threadProducer, so that threadConsumer can respond in time when wakeAll() is in threadProducer, or it will lose the data of the first time a false throw is made.

When terminating a thread, if the threadProducer thread is terminated first in the order above, terminate () must be used to force the end of the threadConsurner thread because the threadConsumer may still be blocked by a conditional wait and will not be able to terminate the thread properly.

Please give some compliment to those who like this article and let's work together.

39 original articles were published, 12 were praised, and 5732 were visited
Private letter follow

Posted by usawh on Wed, 11 Mar 2020 20:12:44 -0700