基于信号量的线程同步
信号量(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();
}