双缓冲区数据采集和读取线程类设计

基于信号量的线程同步

信号量(QSemaphore)是一种限制对共享资源进行访问的线程同步机制,用来保护一定数量的相同的资源,可以多次使用,适用于Producer/Consumer模型

//QSemaphore是实现信号量功能的类,其主要函数如下:
1.acquire(int n):尝试获取n个资源,
2.release(int n):尝试释放n个资源
3.int available():返回可用的资源个数
4.bool tryAcquire(int n=1):尝试获取n个资源,不成功时不阻塞线程

数据采集线程

将采集卡采集的数据读取到缓冲区中

数据读取线程

读取已存满数据的缓冲区中的数据并传递给主线程显示,一般采用信号与槽机制与主线程交互

数据传递时,采用指针形式一次传递一个缓冲区的数据

signal:
void newValue(int *data,int count,int seq);

写操作:

初始化确保有两个空的缓冲区可用

写数据时,首先获得一个空的缓冲区,将数据写入后,释放一个满的缓冲区信号

读操作:

初始化确保满的缓冲为零

读数据时,获取一个满的缓冲区。将数据读出来后,释放一个空的缓冲区信号

启动线程

启动线程时,必须先启动threadConsumer线程,再启动threadProducer线程,否则可能丢失掉第一个缓冲区的数据

结束线程

结束线程时,都采用terminate()强制结束,否则因此线程自锁可能会导致线程无法结束的问题

注意

实际生产中,数据读取进程的速度必须快过数据写入缓冲区的速度,否则会导致数据丢失的问题

完整代码结构如下:

1.QMyThread.h

#ifndef QMYTHREAD_H
#define QMYTHREAD_H

#include<QThread>

class QThreadDAQ : public QThread
{
    Q_OBJECT

private:
    bool m_stop=false;//停止线程

protected:
    void run() Q_DECL_OVERRIDE;

public:
    QThreadDAQ();
    void stopThread();

};


class QThreadShow:public QThread
{
    Q_OBJECT
private:
    bool m_stop=false;
protected:
    void run() Q_DECL_OVERRIDE;

public:
    QThreadShow();
    void stopThread();
signals:
    void newValue(int *data,int count,int seq);

};
#endif // QMYTHREAD_H

2.QMyThread.cpp

#include "qmythread.h"
#include<QSemaphore>



//默认定义内容
const int BufferSize=24;
int buffer1[BufferSize];
int buffer2[BufferSize];

int curBuf=1;//当前正在写入的buffer
int bufNo=0;//采集的缓冲区序号
quint8 counter=0;//数据生成器

QSemaphore emptyBufs(2);//信号量,空的缓冲区个数, 初始资源个数为2
QSemaphore fullBufs;//满的缓冲区个数,初始资源为0

QThreadDAQ::QThreadDAQ()
{

}

void QThreadDAQ::run()
{
    m_stop=false;//启动线程时,令m_stop=false
    bufNo=0;//缓冲区序号
    curBuf=1;//当前写入使用的缓冲区
    counter=0;//数据生成器

    //线程启动时判断emptyBufs的可用资源数。判断后置为2
    int n=emptyBufs.available();
    if(n<2)//保证线程启动时emptyBufs.available==2
    {
        emptyBufs.release(2-n);

    }

    while (!m_stop)
    {
        emptyBufs.acquire();//获取一个空的缓冲区
        for(int i=0;i<BufferSize;i++)
        {
            if(curBuf==1)
            {
                buffer1[i]=counter;//向缓冲区写入数据
            }
            else
            {
                buffer2[i]=counter;
            }
            counter++;//模拟数据采集卡产生的数据
            msleep(20);
        }

        bufNo++;//缓冲区计数-------第几次写入
        if(curBuf==1)//切换当前写入缓冲区
        {
            curBuf=2;
        }
        else {
            curBuf=1;
        }

        fullBufs.release();//有了一个满的缓冲区,available==1
        //表示一个缓冲区写满了,然后在QThreadShow线程中使用fullBufs.acquire就可以获得一个资源,读取已将写满的缓冲区内的数据。

    }
    quit();

}

void QThreadDAQ::stopThread()
{
    m_stop=true;

}

QThreadShow::QThreadShow()
{

}

//用于监测是否已经有写满数据的缓冲区,只要有缓冲区写满了数据,就立刻读出数据,然后释放这个缓冲区给QThreadDAQ线程用于写入
void QThreadShow::run()
{
    m_stop=false;
    //初始化使得fullBufs.available的可用资源为0,即线程刚启动时是没有资源的
    int n=fullBufs.available();
    if(n>0)
    {
        fullBufs.acquire(n);//将fullBufs.available的可用资源个数初始化为0
    }
    
    while(!m_stop)
    {
        fullBufs.acquire();//等待有缓冲区满,当fullBufs.available==0阻塞
        int bufferData[BufferSize];
        int seq=bufNo;

        if(curBuf==1)
            for (int i = 0; i < BufferSize; ++i) {
                bufferData[i]=buffer2[i];//快速拷贝缓冲区数据
            }
        else {
            for (int i = 0; i < BufferSize; ++i) {
                bufferData[i]=buffer1[i];
            }
        }
        emptyBufs.release();//释放一个空缓冲区
        emit newValue(bufferData,BufferSize,seq);//给主线程传递数据
    }
    quit();
}

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

3.dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include<QTimer>
#include"qmythread.h"
#include<QCloseEvent>

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

    //定义两个线程类
private:
    QThreadDAQ threadProducer;
    QThreadShow threadConsumer;

protected:

    void closeEvent(QCloseEvent *e);

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 *data,int count,int bufNo);

    void on_btnStartThread_clicked();

    void on_btnStopThread_clicked();

    void on_btnClearText_clicked();


private:
    Ui::Dialog *ui;
};

#endif // DIALOG_H

 4.dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"
#include<QDateTime>

void Dialog::closeEvent(QCloseEvent *e)
{
    if(threadProducer.isRunning())
    {
        threadProducer.terminate();
        threadProducer.wait();
    }
    if(threadConsumer.isRunning())
    {
        threadConsumer.terminate();
        threadConsumer.wait();
    }

    e->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,int)),this,SLOT(onthreadB_newValue(int*,int,int)));
}

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

void Dialog::onthreadA_started()
{
    ui->lab_ThreadA->setText("thread Producer状态:started!");
}

void Dialog::onthreadA_finished()
{
    ui->lab_ThreadA->setText("thread Producer状态:finished!");
}

void Dialog::onthreadB_started()
{
    ui->lab_ThreadB->setText("thread Consumer状态:started!");
}

void Dialog::onthreadB_finished()
{
    ui->lab_ThreadB->setText("thread Consumer状态:finished!");
}

void Dialog::onthreadB_newValue(int *data, int count, int bufNo)
{
    //读取threadConsumer传递的缓冲区的数据
    QString str=QString::asprintf("第 %d 个缓冲区:",bufNo);
    for(int i=0;i<count;i++)
    {
        str=str+QString::asprintf(" %d ",*data);
        data++;
    }
    QDateTime currentTime=QDateTime::currentDateTime();
    QString timeStr=currentTime.toString("hh:mm:ss:zzz");
    str.append(timeStr);
    str=str+'\n';
    ui->textEdit->append(str);

}

void Dialog::on_btnStartThread_clicked()
{
    //启动线程
    threadConsumer.start();
    threadProducer.start();
}

void Dialog::on_btnStopThread_clicked()
{
    //结束线程
    threadConsumer.terminate();
    threadConsumer.wait();
    threadProducer.terminate();
    threadProducer.wait();
}

void Dialog::on_btnClearText_clicked()
{
    ui->textEdit->clear();

}

5.main.cpp

#include "dialog.h"
#include <QApplication>

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

    return a.exec();
}

 界面样式

 

  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值