子线程不能读取和修改主线程的UI控件数据
空间数据的修改和显示更新只能在主线程操作
也就是UI操作只能在主线程中
子线程非run()成员函数,在哪个线程调用就显示哪个线程的ID
myThread : public QThread
{
public:
void func(); // 子线程类中的其他成员函数,实际上是在主线程中运行的
protected:
void run() // 子线程类中的run函数是在子线程中运行的,子线程类中的其他成员函数,实际上是在主线程中运行的
}
void myThread::func()
{
qDebug()<<currentThreadId();
}
void myThread::run()
{
qDebug()<<currentThreadId(); // 子线程id: 0x1de0
func(); // 子线程id: 0x1de0
}
Widget::Widget(QObject *parent)
:QWidget(parent)
{
qDebug()<<currentThreadId(); // 主线程id: 0x3004
myThread m_thread;
m_thread.start();
m_thread.func(); // 主线程id: 0x3004 (虽然是子线程类成员函数)
}
quit
应用程序或线程安全的取消事件处理队列的执行,并随后使线程退出(如果只希望结束线程并保证它安全的清理资源,最好使用quit)
当调用quit()方法时,线程会等待一段时间来完成当前正在运行的任务和冲洗缓存,然后终止线程并发出finished()信号,告诉其他对象该线程已终止
如果线程正在执行事件循环,则该函数会等待事件循环结束后退出线程(如果线程没有运行时间循环,则该函数不会有任何效果)
eixt
直接停止线程,未处理完的事件将被丢弃并静默忽略
该方法没有给予线程任何机会来清理自己的资源和数据,并可能导致应用程序的崩溃(尽量避免使用此方法)
该方法不管线程是否在事件循环中运行,会发送一个finished信号以通知线程已经退出
terminate
可以强制终止一个线程,但是相比exit更加暴力
该方法会强制关闭线程并释放正在使用的所有资源,可以在某些情况下正常处理一个卡住的线程,但也可能导致数据丢失、资源泄漏以及应用程序不稳定(尽量避免使用该方法)
掷骰子
#ifndef TDICETHREAD_H
#define TDICETHREAD_H
#include
#include
#include
class TDiceThread : public QThread
{
Q_OBJECT
public:
explicit TDiceThread(QObject *parent = nullptr);
void diceBegin(); // 开始投骰子
void dicePause(); // 暂停投
void stopThread(); // 结束线程
signals:
void newValue(int seq,int diceValue); // 产生新点数的信号
private:
int m_seq=0; //
int m_diceValue; // 骰子点数
bool m_paused=true; // 暂停投骰子
bool m_stop=false; // 停止线程 退出run
protected:
void run();
};
#endif // TDICETHREAD_H
#include "tdicethread.h"
TDiceThread::TDiceThread(QObject *parent)
: QThread{parent}
{
}
void TDiceThread::diceBegin() // 开始投骰子
{
m_paused=false;
}
void TDiceThread::dicePause() // 暂停投
{
m_paused=true;
}
void TDiceThread::stopThread() // 结束线程
{
m_stop=true;
}
void TDiceThread::run()
{
m_stop=false;
m_paused=true;
while (!m_stop)
{
if (!m_paused)
{
m_diceValue=QRandomGenerator::global()->bounded(1,7); // 生成随机数
m_seq++;
emit newValue(m_seq,m_diceValue); //发送信号
}
msleep(500); // 线程休眠500ms
}
quit(); // m_stop==true时结束线程任务 等价于exit(0)
}
#ifndef WIDGET_H
#define WIDGET_H
#include
#include
#include "tdicethread.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
TDiceThread *threadA; // 工作线程
protected:
void closeEvent(QCloseEvent *event);
private slots:
void threadA_started();
void threadA_finished();
void threadA_newValue(int seq,int diceValue);
void on_pushButton_clicked();
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
void on_pushButton_4_clicked();
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
threadA=new TDiceThread(this);
connect(threadA,&TDiceThread::started,this,&Widget::threadA_started); // started信号继承自QThread
connect(threadA,&TDiceThread::finished,this,&Widget::threadA_finished); // finished信号继承自QThread
connect(threadA,&TDiceThread::newValue,this,&Widget::threadA_newValue); // 通过信号和槽传递参数
}
Widget::~Widget()
{
delete ui;
}
void Widget::threadA_started()
{
qDebug()<<"====== threadA started ======";
}
void Widget::threadA_finished()
{
qDebug()<<"====== threadA finished ======";
}
void Widget::threadA_newValue(int seq,int diceValue)
{
QString str=QString::asprintf("the %d times, point num is:%d",seq,diceValue);
qDebug()<<str;
}
void Widget::closeEvent(QCloseEvent *event)
{
qDebug()<<"try to close main window";
if (threadA->isRunning())
{
threadA->terminate(); // 强制终止线程
threadA->wait(); // 等待子线程结束完成,主线程再执行
}
event->accept();
}
void Widget::on_pushButton_clicked()
{
qDebug()<<"start threadA";
threadA->start();
}
void Widget::on_pushButton_2_clicked()
{
qDebug()<<"quit threadA";
threadA->stopThread(); // 内部自动调用quit()
}
void Widget::on_pushButton_3_clicked()
{
qDebug()<<"start task";
threadA->diceBegin();
}
void Widget::on_pushButton_4_clicked()
{
qDebug()<<"pause task";
threadA->dicePause();
}
创建
工作线程类必须继承自QObject,而不是QThread
工作线程的实例对象不能添加父对象(Worker worker=new Worker,(不能是new Worker(this)))
可以把多个任务,move到同一个子线程中,但是多个任务不能同时执行
连接
方法1:可以直接将子线程的QThread::started信号,与工作任务类对象的&Task::doWork(槽)函数关联
方法2:见moveToThread例子2,主线程主动发送自定义的statring信号,与工作任务类对象的&Task::doWork(槽)函数关联
#ifndef CLASS1_H
#define CLASS1_H
#include
#include
#include
class class1 : public QObject
{
Q_OBJECT
public:
explicit class1(QObject *parent = nullptr);
signals:
public slots:
void doWork();
};
#endif // CLASS1_H
#include "class1.h"
class1::class1(QObject *parent)
: QObject{parent}
{
}
void class1::doWork()
{
qDebug()<<"class1 sub thread"<<QThread::currentThread();
}
#ifndef WIDGET_H
#define WIDGET_H
#include
#include
#include "class1.h"
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
class1 *worker1;
QThread *thread1;
};
#endif // WIDGET_H
#include "widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
worker1=new class1(); // QObject::moveToThread: Cannot move objects with a parent
thread1=new QThread(this);
worker1->moveToThread(thread1);
connect(thread1,&QThread::started,worker1,&class1::doWork);
connect(worker1,&class1::destroyed,thread1,&QThread::quit);
thread1->start();
}
Widget::~Widget()
{
}
只将moveToThread例子1中,Widget类中的.cpp文件进行如下修改,程序运行结果是一样的
#include "widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
worker1=new class1(); // QObject::moveToThread: Cannot move objects with a parent
thread1=new QThread(this);
worker1->moveToThread(thread1);
// connect(thread1,&QThread::started,worker1,&class1::doWork);
connect(this,&Widget::starting,worker1,&class1::doWork);
connect(worker1,&class1::destroyed,thread1,&QThread::quit);
emit starting();
thread1->start();
}
Widget::~Widget()
{
}
ITC技术:线程间通信
信号与槽 机制可以实现线程间通信
每次只能有一个线程获得互斥量的使用权限
QMutex
lock 阻塞调用,直到互斥量被解锁
trylock 非阻塞调用,加锁成功返回true,如果其他线程已经锁定了这个互斥量,就返回false不等待,timeout最多等待timeout秒
#include
#include
void QMutex::lock() // 锁定互斥量,一致等待
void QMutex::unlock() // 解锁互斥量
bool QMutex::tryLock() // 尝试锁定互斥量,不等待
bool QMutex::tryLock(int timeout) // 尝试锁定互斥量,最多等待timeout毫秒
QMutexLocker
QMutexLocker是一个简化了互斥锁处理的类,QMutexLocker的构造函数接受互斥量作为参数将其锁定,QMutexLocker的析构函数则将此互斥量解锁,所以在QMutexLocker实例变量的生存周期内代码片段会得到保护,自动进行互斥量的解锁加锁
void TDiceThread::run()
{
m_stop=false;
m_paused=true;
while (!m_stop)
{
if (!m_paused)
{
/* ====== QMutex ====== */
mutex.lock();
m_diceValue=0;
// 对多个随机数求平均值
for (int i=0;i<5;i++)
{
m_diceValue+=QRandomGenerator::global()->bounded(1,7); // 生成随机数
}
m_diceValue/=5;
m_seq++;
mutex.unlock();
/* ====== QMutexLocker ====== */
QMutexLocker locker(&mutex); // 锁定mutex,超过if语句块范围就解锁
m_diceValue=0;
// 对多个随机数求平均值
for (int i=0;i<5;i++)
{
m_diceValue+=QRandomGenerator::global()->bounded(1,7); // 生成随机数
}
m_diceValue/=5;
m_seq++;
}
msleep(500); // 线程休眠500ms
}
quit(); // m_stop==true时结束线程任务 等价于exit(0)
}
bool TDiceThread::getDiceValue(int *seq, int *diceValue)
{
if (mutex.tryLock(100)) // 尝试锁定互斥量
{
*seq=m_seq;
*diceValue=m_diceValue;
mutex.unlock();
return true;
}
else
{
return false;
}
}
// main widget.cpp
int seq=-1;
int diceValue=-1;
threadA->getDiceValue(&seq,&diceValue);
qDebug()<<"main thread get dicevalue: "<<seq<<" time, value: "<<diceValue;
其他例子 – 伪代码
#include "mythread.h"
QMutex mutex;
myThreadDAQ::myThreadDAQ(QObject *parent)
: QThread{parent}
{
}
void myThreadDAQ::run() // 负责采集数据的线程
{
mutex.lock();
// get_data_and_write_in_buffer() // 数据写入buffer
mutex.unlock();
}
myThreadShow::myThreadShow(QObject *parent)
: QThread{parent}
{
}
void myThreadShow::run() // 负责显示数据的线程
{
mutex.lock();
// show_buffer(); // 读取buffer里的数据并显示
mutex.unlock();
}
myThreadSaveFile::myThreadSaveFile(QObject *parent)
: QThread{parent}
{
}
void myThreadSaveFile::run() // 负责保存数据的线程
{
mutex.lock();
// save_buffer_toFile(); // 读取buffer里的数据并保存到文件
mutex.unlock();
}
互斥锁每次只能有一个线程获得互斥量的使用权限,如果一个程序中有多个线程读取某个变量,使用互斥量时,必须排队
实际上若只是读取一个变量,可以让多个线程同时访问,这种情况下使用互斥锁就会降低程序的性能
QReadWriteLock
基于读或写的方式进行代码片段锁定,在多个线程读写一个共享数据时,读写锁可以解决互斥锁存在的唯一线程访问的缺点
QReadWriteLock以读或写锁定的同步方法允许以读或写的方式保护一段代码,可以允许多个线程以只读的方式同步访问资源,但是只要有一个线程在以写的方式访问资源,其他线程想要无论是写或读都必须等待,直到写操作完成
也就是读和写不能同时进行
void lockForRead() // 以只读方式锁定资源,如果有其他线程以写入方式锁定资源,这个函数会阻塞
void lockForWrite() // 以写入方式锁定资源,如果有其他线程以读或写的方式锁定资源,这函数会阻塞
void unlock() // 解锁
bool tryLockForRead() // 不等待
bool tryLockForRead(int timeout) // 不等待
bool tryLockForWrite() // 不等待
bool tryLockForWrite(int timeout) // 不等待
例子 – 伪代码
QReadWriteLock rwLocker;
myThreadDAQ::myThreadDAQ(QObject *parent)
: QThread{parent}
{
}
void myThreadDAQ::run() // 负责采集数据的线程
{
rwLocker.lockForWrite();
// get_data_and_write_in_buffer() // 数据写入buffer
rwLocker.unlock();
}
myThreadShow::myThreadShow(QObject *parent)
: QThread{parent}
{
}
void myThreadShow::run() // 负责显示数据的线程
{
rwLocker.lockForRead();
// show_buffer(); // 读取buffer里的数据并显示
rwLocker.unlock();
}
myThreadSaveFile::myThreadSaveFile(QObject *parent)
: QThread{parent}
{
}
void myThreadSaveFile::run() // 负责保存数据的线程
{
rwLocker.lockForRead();
// save_buffer_toFile(); // 读取buffer里的数据并保存到文件
rwLocker.unlock();
}
互斥锁和读写锁的问题: 一个线程解锁资源后,不能即使通知其他线程
条件变量(条件等待)QWaitCondition通过与QMutex或QReadWriteLock结合使用,可以使一个线程在满足一定条件时通知其他其他多个线程,使其他多个线程及时进行响应,这样比只使用互斥量或读写锁效率更高一些
QWaitConidtion 一般用于生产者/消费者模型,生产者生产数据,消费者使用数据
bool wait(QMutex *lockedMutex, unsigned long time) // 释放互斥量,并等待唤醒
bool wait(QReadWriteLock *lockedReadWriteLock, unsigned long time) // 释放读写锁,并等待唤醒
void wakeAll() // 唤醒所有处于等待状态的现场,唤醒线程的顺序不确定,由操作系统的调度策略决定
void wakeOne() // 唤醒一个处于等待状态的线程,唤醒哪个线程不确定,由操作系统的调度策略决定
条件变量与 互斥锁 / 读写锁的作用机制
https://blog.csdn.net/L_fengzifei/article/details/129220295 !!!
#ifndef TDICETHREAD_H
#define TDICETHREAD_H
#include
#include
class TDiceThread : public QThread // 生成骰子点数
{
Q_OBJECT
public:
explicit TDiceThread(QObject *parent = nullptr);
signals:
protected:
void run();
};
class TValueThread:public QThread // 获得骰子点数
{
Q_OBJECT
public:
explicit TValueThread(QObject *parent=nullptr);
signals:
void newValue(int seq,int diceValue);
protected:
void run();
};
class TPictureThread:public QThread // 获得骰子点数,生成对应的图片文件名
{
Q_OBJECT
public:
explicit TPictureThread(QObject *parent=nullptr);
signals:
void newPicture(QString &picName);
protected:
void run();
};
#endif // TDICETHREAD_H
#include "tdicethread.h"
#include
#include
#include
static QReadWriteLock rwLocker; // 读写锁
static QWaitCondition waiter; // 等待条件
static int seq=0;
static int diceValue=0;
TDiceThread::TDiceThread(QObject *parent)
: QThread{parent}
{
}
void TDiceThread::run()
{
seq=0;
while (1)
{
rwLocker.lockForWrite(); // 以写入方式锁定
diceValue =QRandomGenerator::global()->bounded(1,7);
seq++;
rwLocker.unlock(); // 读写锁解锁
waiter.wakeAll(); // 唤醒其他等待的线程
msleep(500); // 每隔500ms生成一次数据,新数据生成后唤醒所有等待的线程
}
}
TValueThread::TValueThread(QObject *parent)
: QThread{parent}
{
}
void TValueThread::run()
{
while (1)
{
rwLocker.lockForRead(); // 以只读方式锁定
/*
以读写锁作为参数
当不满足条件时,内部首先会释放读写锁rwLocker,然后阻塞线程,即当前线程进入等待状态,从而使得其他线程可以锁定rwLocker
当满足条件(TDiceThread waiter.wakeAll)唤醒所有等待的线程后,TValueThread会先对rwLocker进行锁定,然后(解除)退出阻塞状态,执行后面的代码
等待变量一旦对读写锁加锁,就不会再主动解锁,只有第一次不满足条件时才会主动释放
*/
waiter.wait(&rwLocker); // 等待被唤醒
emit newValue(seq,diceValue);
rwLocker.unlock(); // 解锁
}
}
TPictureThread::TPictureThread(QObject *parent)
: QThread{parent}
{
}
void TPictureThread::run()
{
while (1)
{
rwLocker.lockForRead(); // 以只读方式锁定
waiter.wait(&rwLocker); // 等待被唤醒
QString fileName=QString::asprintf(":/images/images/d%d.jpg",diceValue);
emit newPicture(fileName);
rwLocker.unlock();
}
}
#ifndef WIDGET_H
#define WIDGET_H
#include
#include
#include "tdicethread.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
TDiceThread *execTaskthread;
TValueThread *getValueThread;
TPictureThread *getPicThread;
private slots:
void thread_started();
void thread_finished();
void getNewValue(int seq, int diceValue); // connect getValueThread::newValue singal
void getNewPic(QString &picName); // connect getPicThread::newPicture singal
void on_pushButton_clicked();
void on_pushButton_2_clicked();
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
qDebug()<<"main thread ID: "<<QThread::currentThreadId();
execTaskthread=new TDiceThread(this);
getValueThread=new TValueThread(this);
getPicThread=new TPictureThread(this);
connect(execTaskthread,&TDiceThread::started,this,&Widget::thread_started);
connect(execTaskthread,&TDiceThread::finished,this,&Widget::thread_finished);
connect(getValueThread,&TValueThread::newValue,this,&Widget::getNewValue);
connect(getPicThread,&TPictureThread::newPicture,this,&Widget::getNewPic);
}
Widget::~Widget()
{
delete ui;
}
void Widget::thread_started()
{
qDebug()<<"execute task thread started";
qDebug()<<"main thread try to get task thread ID: "<<QThread::currentThreadId();
}
void Widget::thread_finished()
{
qDebug()<<"task thread finished and quit";
}
void Widget::getNewValue(int seq, int diceValue) // connect getValueThread::newValue singal
{
qDebug()<<"receive a singal from getValueThread,ID:"<<QThread::currentThreadId();
QString str=QString::asprintf("the %dth times, num point is %d",seq,diceValue);
qDebug()<<str;
}
void Widget::getNewPic(QString &picName) // connect getPicThread::newPicture singal
{
qDebug()<<"receive a singal from getPicThread,ID:"<<QThread::currentThreadId();
QPixmap pic(picName);
ui->label->resize(143,143);
ui->label->setScaledContents(true);
ui->label->setPixmap(pic);
// ui->label->setScaledContents();
}
void Widget::on_pushButton_clicked()
{
getValueThread->start();
if (!getPicThread->isRunning())
{
getPicThread->start();
}
if (!execTaskthread->isRunning())
{
execTaskthread->start();
}
}
void Widget::on_pushButton_2_clicked()
{
/*
??? 为什么只用终止一个线程
*/
execTaskthread->terminate();
execTaskthread->wait();
}
互斥量只能被锁定一次
信号量可以被多次使用,信号量通常用来保护一定数量的相同资源(如,数据采集时的双缓冲区)
构造函数,设置初始值n,表示可用资源的个数,n表示信号量主要保护的共享资源,(资源如何分配,是内部处理的问题)
acquire 以阻塞方式获取一个资源,信号量的资源数-1
release 释放一个资源,信号量的资源数+1
QSemaphore(int n=0) // 构造函数,设置资源数
void acquire(int n=1) // 尝试获得n个资源,阻塞等待
int availavle() // 返回当前信号量可用资源的个数
void release(int n=1) // 释放n个资源
bool tryAcquire(int n=1) // 尝试获取n个资源,不等待
bool tryAcquire(int n, int timeout) // 尝试获得n个资源,最多等待timeout毫秒
// 例子
QSemaphore WC(5) // WC.available()==5 初始资源个数5
WC.acquire(4); // WC.available()==1,用了4个还剩1个
WC.release(2); // WC.available()==3,释放2个还剩3个
WC.acquire(3); // WC.available()==0,用了3个,剩余0个
WC.tryAcquire(1); // WC.available()==0(-1),返回false
WC.tryAcquire(1,30000); // WC.available()==0 ,等待30000无果,返回false
WC.acquire(); // WC.available()==0,没有资源可用,阻塞等待
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include
#include
#include
/*
数据采集线程,连续数据采集,将采集的数据存储到缓冲区
数据处理线程,及时处理已写满数据的缓冲区
*/
class TDaqThread: public QThread // 数据采集线程
{
Q_OBJECT
public:
explicit TDaqThread(QObject *parent = nullptr);
void stopThread();
signals:
private:
bool m_stop=false;
protected:
void run();
};
class TprocessThread : public QThread // 数据处理线程
{
Q_OBJECT
public:
explicit TprocessThread(QObject *parent = nullptr);
void stopThread();
signals:
void dataAvailable(int bufferSeq,int *bufferData,int pointCount);
private:
bool m_stop=false;
protected:
void run();
};
#define BUFFER_SIZE 10 // 缓冲区数据点个数
extern int buffer1[BUFFER_SIZE];
extern int buffer2[BUFFER_SIZE];
extern int curBufNum; // 当前正在写入的缓冲区编号
extern int bufSeq; // 缓冲区序号
extern QSemaphore emptyBufs; // 信号量,空的缓冲区个数,初始资源个数为2
extern QSemaphore fullBufs; // 信号量,满的缓冲区个数,初始资源个数为0
#endif // MYTHREAD_H
#include "mythread.h"
#include "cstring"
int buffer1[BUFFER_SIZE];
int buffer2[BUFFER_SIZE];
int curBufNum=1; // 当前正在写入的缓冲区编号
int bufSeq=0; // 缓冲区序号
QSemaphore emptyBufs(2); // 信号量,空的缓冲区个数,初始资源个数为2
QSemaphore fullBufs; // 信号量,满的缓冲区个数,初始资源个数为0
TDaqThread::TDaqThread(QObject *parent)
: QThread{parent}
{
memset(buffer1,0,sizeof(buffer1));
}
void TDaqThread::stopThread()
{
m_stop=true;
}
void TDaqThread::TDaqThread::run()
{
curBufNum=1; // 当前正在写入的缓冲区编号
bufSeq=0; // 缓冲区序号
int counter=0; // 模拟数据
int n=emptyBufs.available(); // 当前缓冲区的可用资源个数
if (n<2) // 确保线程启动时 emptyBufs.availabel()==2
{
emptyBufs.release(2-n);
}
m_stop=false;
while (!m_stop)
{
emptyBufs.acquire(); // 获得一个空的缓冲区
int *buf=curBufNum==1?buffer1:buffer2; // 设置当前工作缓冲区指针
for (int i=0;i<BUFFER_SIZE;i++) // 产生一个缓冲区的数据
{
*buf=counter;
buf++;
counter++;
msleep(10);
}
bufSeq++; // 缓冲区序号
curBufNum=curBufNum==1?2:1; // 切换当前写入的缓冲区编号
fullBufs.release(); // 释放一个资源,有了一个慢的缓冲区
}
}
TprocessThread::TprocessThread (QObject *parent)
: QThread{parent}
{
}
void TprocessThread::run()
{
int n=fullBufs.available();
if (n>0)
{
fullBufs.acquire(n); // 将fullBufs可用资源个数初始化为0
}
int buffData[BUFFER_SIZE];
m_stop=false;
while (!m_stop)
{
if (fullBufs.tryAcquire(1,50)) // 尝试获得一个资源,最多等待50ms
{
int *bufferFull=curBufNum==1?buffer2:buffer1; // 获得已写满的缓冲区指针
for (int i=0;i<BUFFER_SIZE;i++,bufferFull++) // 模拟数据处理
{
buffData[i]=*bufferFull;
}
emptyBufs.release(); // emptyBufs 释放一个资源,可用空资源数+1
int pointCount=BUFFER_SIZE;
emit dataAvailable(bufSeq,buffData,pointCount); // 发射信号
}
}
}
void TprocessThread::stopThread()
{
m_stop=true;
}
#ifndef WIDGET_H
#define WIDGET_H
#include
#include "mythread.h"
#include
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
TDaqThread *threadDAQ; // 数据采集线程
TprocessThread *threadShow; // 数据处理线程
private slots:
void readBuffer(int bufferSeq,int *bufferData,int pointCount);
void threadDAQ_started();
void threadDAQ_finished();
void on_pushButton_clicked();
void on_pushButton_2_clicked();
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
threadDAQ=new TDaqThread(this); // 数据采集线程
threadShow=new TprocessThread(this); // 数据处理线程
connect(threadDAQ,&TDaqThread::started,this,&Widget::threadDAQ_started);
connect(threadDAQ,&TDaqThread::finished,this,&Widget::threadDAQ_finished);
connect(threadShow,&TprocessThread::dataAvailable,this,&Widget::readBuffer);
}
Widget::~Widget()
{
delete ui;
}
void Widget::readBuffer(int bufferSeq,int *bufferData,int pointCount)
{
QString str=QString::asprintf("the %d th buffer block",bufferSeq);
qDebug()<<str;
QString bufferData_str;
for (int i=0;i<pointCount;i++)
{
// qDebug()<<*bufferData;
bufferData_str+=QString::asprintf("%d, ",*bufferData);
bufferData++;
}
qDebug().noquote()<<bufferData_str;
}
void Widget::threadDAQ_started()
{
qDebug()<<"threadDAQ started";
}
void Widget::threadDAQ_finished()
{
qDebug()<<"threadDAQ finished";
}
void Widget::on_pushButton_clicked()
{
threadShow->start();
threadDAQ->start();
}
void Widget::on_pushButton_2_clicked()
{
threadShow->stopThread();
threadDAQ->stopThread();
}
https://www.bilibili.com/video/BV1iN411f7dY/?p=12&spm_id_from=pageDriver&vd_source=7155082256127a432d5ed516a6423e20 – 还没看