欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Qthread多线程程序

程序员文章站 2022-05-10 12:09:11
...

Qthread多线程程序

13.1 Qthread创建多线程程序。

        Qthread类提供不依赖于平台的管理线程的方法。一个qthread类的对象管理一个线程。一般从Qthread继承一个自定义。从定义虚函数run(),在run()函数里实现线程需要完成的任务。

        想应用程序的线程称为主线程。额外创建的线程称为工作线程。一般在主线程里创建工作线程。并调用start()开始执行工作线程的任务。start()会在内部调用run()函数。进入工作线程的事件循环。在run()函数里调用exit()或者quite()可以结束线程的事件循环,或在主线程里调用TermiQ thread类主要接口,函数,信号和槽见表。

        Qthread类的主要接口函数,信号和槽函数见表13-1

Qthread多线程程序

QThread shi QObject 的子类,所以可以使用信号与槽机制。QThread自定义了 started()和Finished()两个信号。Started()信号,在线程开始执行之前发射。

13.1.2 掷色子的线程 QDiceThread

        作为实例定义一个掷色子的线程类QDiceThread,类的声明部分如下:

#ifndef QDICETHREAD_H
#define QDICETHREAD_H

#include    <QThread>

class QDiceThread : public QThread
{
    Q_OBJECT
private:
    int     m_seq=0;//掷骰子次数序号
    int     m_diceValue;//骰子点数
    bool    m_Paused=true; //掷一次骰子
    bool    m_stop=false; //停止线程
protected:
    void    run() Q_DECL_OVERRIDE;  //线程任务
public:
    QDiceThread();

    void    diceBegin();//掷一次骰子
    void    dicePause();//暂停
    void    stopThread(); //结束线程
signals:
    void    newValue(int seq,int diceValue); //产生新点数的信号
};

#endif // QDICETHREAD_H

 重载虚函数run(),在此函数里完成线程的主要任务。

自定义diceBegin(),dicePause(),stopThread() 三种函数用于线程控制。这三个函数由主线程调用。

        定义一个信号 newValue(int seq,int diceValue) 用于掷一次骰子得到新的点数之后发射信号,由主线程的槽函数响应以获取值。

        QDiceThread类的实现代码如下:

#include "qdicethread.h"
#include    <QTime>

QDiceThread::QDiceThread()
{

}

void QDiceThread::diceBegin()
{ //开始掷骰子
    m_Paused=false;
}

void QDiceThread::dicePause()
{//暂停掷骰子
    m_Paused=true;
}

void QDiceThread::stopThread()
{//停止线程
    m_stop=true;
}

void QDiceThread::run()
{//线程任务
    m_stop=false;//启动线程时令m_stop=false
    m_seq=0; //掷骰子次数
    qsrand(QTime::currentTime().msec());//随机数初始化,qsrand是线程安全的

    while(!m_stop)//循环主体
    {
        if (!m_Paused)
        {
            m_diceValue=qrand(); //获取随机数
            m_diceValue=(m_diceValue % 6)+1;
            m_seq++;
            emit newValue(m_seq,m_diceValue);  //发射信号
        }
        msleep(500); //线程休眠500ms
    }

//  在  m_stop==true时结束线程任务
    quit();//相当于  exit(0),退出线程的事件循环
}

       其中run()是线程任务的实现部分。线程开始就执行run()函数。Run函数一般是事件循环过程。根据各种条件或事件处理各种任务。当run函数退出时,线程的事件循环就结束了。

        在run函数里,初始化变量 m_stop 和m_seq,用qsrand()函数对随机数种子初始化。run()函数的主循环体是一个while循环。在主线程调用stopthread()函数使 m_stop为true,才会退出while循环,调用quit()之后结束线程。

        在 while 循环体内,又根据m_Paused 判断当前是否需要掷骰子,如果需要掷骰子,则用随机函数生成一次骰子的点数m_diceValue,然后发射信号newValue(),将m_seq和m_diceValue作为信号参数传递出去。主线程可以设计曹函数与此信号关联,获取这个两个值并进行显示。

13.1.3 掷骰子的多线程应用程序

        使用QDiceThread类,设计一个应用程序samp13_1

Qthread多线程程序

 窗体上方的几个按钮用于控制线程的启动与停止,控制开始与暂停掷骰子。中间的文本框显示次数和点数,右边根据点数显示资源文件里。下方的一个标签根据QDiceThread 的 started()和finished()两个信号显示线程的状态。

        窗口是从QDialog继承的类Dialog,其类定义如下:

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>

#include    "qdicethread.h"

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

private:
//    int     m_counter=0;
    QDiceThread   threadA;

protected:
    void    closeEvent(QCloseEvent *event);

public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();

private slots:
    void    onthreadA_started();
    void    onthreadA_finished();
    void    onthreadA_newValue(int seq, int diceValue);

//    void on_btnStart_clicked();

//    void on_pushButton_clicked();

//    void on_btnSeed_clicked();

//    void on_btnOnce_clicked();

//    void on_btnStop_clicked();

    void on_btnClear_clicked();

    void on_btnDiceEnd_clicked();

    void on_btnDiceBegin_clicked();

    void on_btnStopThread_clicked();

    void on_btnStartThread_clicked();

private:
    Ui::Dialog *ui;
};

#endif // DIALOG_H

这里定义了一个QDiceThread类型的变量threadA,重定义了closeEvent()事件,自定义了3个曹函数。

Dialog类的构造函数代码如下:

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(&threadA,SIGNAL(newValue(int,int)),this,SLOT(onthreadA_newValue(int,int)));
}

 构造函数主要是将threadA的3个信号与Dialog自定义的3个槽函数相关联, 这3个槽函数的代码如下:


void Dialog::onthreadA_started()
{//线程的started()信号的响应槽函数
    ui->LabA->setText("Thread状态:thread started");
}

void Dialog::onthreadA_finished()
{//线程的 finished()信号的响应槽函数
    ui->LabA->setText("Thread状态:thread finished");
}

void Dialog::onthreadA_newValue(int seq,int diceValue)
{//QDiceThread的newValue()信号的响应槽函数,显示骰子次数和点数
    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);
}

started()信号发射时, 表示线程开始执行,在标签里显示状态文字。

finished()信号发射时,表示线程结束执行,在标签里显示状态文字。

newValue()是QDiceThread定义的信号,在掷一次骰子获得新的点数后发射,将掷骰子的次数和点数传递过来。槽函数onthreadA_newValue()获取这两个值并显示在文本框里,再根据点数从资源文件里获取相应的图片并显示。

窗口上5个按钮的代码如下:


void Dialog::on_btnClear_clicked()
{ //清空文本 按钮
    ui->plainTextEdit->clear();
}

void Dialog::on_btnDiceEnd_clicked()
{//暂停 掷骰子按钮
    threadA.dicePause();
    ui->btnDiceBegin->setEnabled(true);
    ui->btnDiceEnd->setEnabled(false);
}

void Dialog::on_btnDiceBegin_clicked()
{//开始 掷骰子按钮
    threadA.diceBegin();
    ui->btnDiceBegin->setEnabled(false);
    ui->btnDiceEnd->setEnabled(true);
}

void Dialog::on_btnStopThread_clicked()
{//结束线程 按钮
    threadA.stopThread();//结束线程的run()函数执行
    threadA.wait();//

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

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

void Dialog::on_btnStartThread_clicked()
{//启动线程 按钮
    threadA.start();

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

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

“启动线程”安妮调用线程的start()函数,start()函数会内部调用run()函数开始线程任务的执行。run()函数将内部变量m_Paused初始化为true,所以启动线程后并不会立即开始掷骰子。

开始 按钮调用diceBegin()函数,使threadA线程内部变量m_Paused变为false,那么run()函数里就开始每隔500毫秒产生一次骰子点数,并发射信号newValue()。

暂停 按钮调用dicePause()函数,使threadA线程内部变量m_Paused变为true, run()函数里不在掷骰子,但是run()函数并没有结束,也就是线程并没有结束。

结束线程按钮调用 stopThread()函数,使threadA 线程内部的m_stop变为true,run()函数体的while循环结束,执行quit()后线程结束。所有,线程结束就是run()函数执行退出。

重载closeEvent()事件,在窗口关闭时确保线程被停止,代码如下。

void Dialog::closeEvent(QCloseEvent *event)
{ //窗口关闭事件,必须结束线程
    if (threadA.isRunning())
    {
        threadA.stopThread();
        threadA.wait();
    }
    event->accept();
}

 

相关标签: Qt