Qt编程技巧总结篇(3)-信号-槽-多线程(二)

  多线程学习,使用QMutex,好了,开整~

主进程与子线程

主进程:经常处于空闲状态,仅处理短小精悍、不怎么占用时间的“小”函数。
子线程:处理主要的计算啊!存储啊!循环等复杂费时的任务。

线程同步

线程同步:子线程中运算量较大的函数在执行的过程中不希望被主线程调用,放置该函数的中断, 因而这类函数需要被保护起来,令其在执行过程中不能被其他线程打断,以保证计算机结果的完整性。
线程同步可以使用:QMutex,QMutexLocker,QReadWriteLock,QReadLocker, QWriteLocker,QWaitCondition,QSemaphore.
这里的例子主要是:QMutexQMutexLocker的应用。

实例与应用

下面的例子是《 QT5.9 c++ 开发指南》中的例子,代码放这里,欢迎参考学习!

子线程.hqdicethread.h文件,

#ifndef QDICETHREAD_H
#define QDICETHREAD_H

#include <QObject>
#include <QThread>
#include <QMutex>
#include <QMutexLocker>
#include <QTime>

class QDiceThread : public QThread
{
    Q_OBJECT
public:
    QDiceThread();
    void diceBegin();                        // 投色子
    void diceEnd();                          // 读色子
    void stopThread();                       // 停止线程
    bool readVal(int *seq,int *diceValue);   // 主线程读色子

private:
    QMutex mutex; //互斥量
    int m_seq = 0;
    int m_diceVal;
    bool m_pause = true;
    bool m_stop=false;

protected:
    void run() Q_DECL_OVERRIDE;
};

#endif // QDICETHREAD_H

子线程.hqdicethread.cpp 文件,

#include "qdicethread.h"

QDiceThread::QDiceThread()
{

}

void QDiceThread::diceBegin()
{
    m_pause = false;
}

void QDiceThread::diceEnd()
{
    m_pause = true;
}


void QDiceThread::stopThread()
{
    m_stop = true;
}

bool QDiceThread::readVal(int *seq, int *diceValue)
{
    if(mutex.tryLock())     //试图锁定一个互斥量,成功返回true。
    {
        *seq = m_seq;
        *diceValue = m_diceVal;
        mutex.unlock();
        return true;
    }
    else
    {
        return false;
    }
}

void QDiceThread::run()
{
    m_stop = false;
    m_seq = 0;
    qsrand(QTime::currentTime().msec()); // 随机数初始化,qsrand是线程安全的

    while(!m_stop)
    {
        /* mutex.lock() 与 mutex.unlock() 配对使用 */
//        if(!m_pause)
//        {
//            mutex.lock();           //锁定互斥量,他将阻塞执行直到其他线程解锁这个互斥量
//            m_diceVal = qrand();
//            m_diceVal = (m_diceVal%6)+1;
//            m_seq++;
//            mutex.unlock();         // 解锁互斥量
//        }

        /* 在一些逻辑复杂的代码中,上述方法配对容易出错,可采用QMutexLocker的方法进行简化 */

        if(!m_pause)
        {
            QMutexLocker Locker(&mutex);
            m_diceVal = qrand();
            m_diceVal = (m_diceVal%6)+1;
            m_seq++;

        }

        msleep(100); // 休眠100ms
    }
}


主线程.h :dialog.h文件,

#ifndef DIALOG_H
#define DIALOG_H

#include <QDebug>
#include <QDialog>
#include <QTimer>
#include "qdicethread.h"

QT_BEGIN_NAMESPACE
namespace Ui { class Dialog; }

class Dialog : public QDialog
{
    Q_OBJECT


// 需要写在前面
private:
    int mSeq,mDiceValue;

    QDiceThread   threadA;
    QTimer  mTimer;//定时器


public:
    Dialog(QWidget *parent = nullptr);
    ~Dialog();

    
private slots:

    void onthreadA_started();
    void onthreadA_finished();

    void onTimeOut(); //定时器处理槽函数????

    void on_btnStartThread_clicked();

    void on_btnDiceBegin_clicked();

    void on_btnDiceEnd_clicked();

    void on_btnStopThread_clicked();

    void on_btnClear_clicked();

private:
    Ui::Dialog *ui;

protected:
    void closeEvent(QCloseEvent *evet);


};
#endif // DIALOG_H

主线程.cpp :dialog.cpp文件,

  • 连接槽函数与信号函数
#include "dialog.h"
#include "ui_dialog.h"


Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
    , ui(new Ui::Dialog)
{
    ui->setupUi(this);

    connect(&threadA,SIGNAL(started()),this,SLOT(onthreadA_started()));
    connect(&threadA,SIGNAL(finished()),this,SLOT(onthreadA_finished()));
    connect(&mTimer,SIGNAL(timeout()),this,SLOT(onTimeOut()));

}

Dialog::~Dialog()
{
    delete ui;
}

void Dialog::onthreadA_started()
{
    ui->LabA->setText("Thread 状态:thread started.");
}

void Dialog::onthreadA_finished()
{
    ui->LabA->setText("Thread 状态:thread finished.");
}

void Dialog::onTimeOut()
{
    int tmpSeq=0,tmpValue=0;
    bool valid = threadA.readVal(&tmpSeq, &tmpValue);
    if(valid && (tmpSeq != mSeq)) // 获取的是有效且新的数据
    {
        qDebug()<<tmpSeq;
        mSeq = tmpSeq;
        mDiceValue = tmpValue;
        QString str=QString::asprintf("第 %d 次投掷点数为:%d",mSeq,mDiceValue);
        ui->plainTextEdit->appendPlainText(str);
        QPixmap pic;
        QString fileName = QString::asprintf(":/dice/images/d%d.jpg",mDiceValue);
        pic.load(fileName);
        ui->LabPic->setPixmap(pic);
    }
}


void Dialog::on_btnStartThread_clicked()
{
    mSeq = 0;
    threadA.start();

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

void Dialog::on_btnDiceBegin_clicked()
{
    threadA.diceBegin();
    mTimer.start(100); //定时器100ms读一次数据
    ui->btnDiceBegin->setEnabled(false);
    ui->btnDiceEnd->setEnabled(true);
}

void Dialog::on_btnDiceEnd_clicked()
{
    threadA.diceEnd();
    mTimer.stop(); // 暂停定时器

    ui->btnDiceBegin->setEnabled(true);
    ui->btnDiceEnd->setEnabled(false);
}

void Dialog::on_btnStopThread_clicked()
{
    threadA.stopThread();
    threadA.wait();

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

void Dialog::on_btnClear_clicked()
{
    ui->plainTextEdit->clear();
}

void Dialog::closeEvent(QCloseEvent *evet)
{
    if(threadA.isRunning())
    {
        threadA.stopThread();
        threadA.wait();
    }
    evet->accept();
}

主函数main.cpp文件,

#include "dialog.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Dialog w;
    w.show();
    return a.exec();
}

小结

  学习,加油,共勉。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部