Qt - 进程/线程 补充进阶

Qt - 进程/线程 补充进阶

  • 多线程
    • QThread 调用机制
    • 主线程 / 子线程 UI
    • 主线程 / 子线程 成员函数调用
    • quit / eixt / terminate
  • QThread例子
    • tdicethread 类
      • .h
      • .cpp
    • widget 类
      • .h
      • .cpp
  • QThread 多线程 第二种创建方法
    • moveToThread 例子1
      • class1 类(工作线程)
        • .h
        • .cpp
      • widget 类(主线程)
        • .h
        • .cpp
    • moveToThread 例子2
  • 线程同步
    • 互斥锁
      • QMutex / QMutexLocker 例子
    • 读写锁
      • QReadWriteLock 例子
    • 条件变量/条件等待
      • QWaitCondition / QReadWriteLock 例子
        • tdicethread 类
          • .h
          • .cpp
        • widget类
          • .h
          • .cpp
    • 信号量
      • QSemaphore 例子
        • mythread 类
          • .h
          • .cpp
        • widget 类
          • .h
          • .cpp
  • 线程池

多线程

QThread 调用机制

Qt - 进程/线程 补充进阶_第1张图片

主线程 / 子线程 UI

子线程不能读取和修改主线程的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 / eixt / terminate

quit

应用程序或线程安全的取消事件处理队列的执行,并随后使线程退出(如果只希望结束线程并保证它安全的清理资源,最好使用quit)
当调用quit()方法时,线程会等待一段时间来完成当前正在运行的任务和冲洗缓存,然后终止线程并发出finished()信号,告诉其他对象该线程已终止
如果线程正在执行事件循环,则该函数会等待事件循环结束后退出线程(如果线程没有运行时间循环,则该函数不会有任何效果)

eixt

直接停止线程,未处理完的事件将被丢弃并静默忽略
该方法没有给予线程任何机会来清理自己的资源和数据,并可能导致应用程序的崩溃(尽量避免使用此方法)
该方法不管线程是否在事件循环中运行,会发送一个finished信号以通知线程已经退出

terminate

可以强制终止一个线程,但是相比exit更加暴力
该方法会强制关闭线程并释放正在使用的所有资源,可以在某些情况下正常处理一个卡住的线程,但也可能导致数据丢失、资源泄漏以及应用程序不稳定(尽量避免使用该方法)

QThread例子

掷骰子

Qt - 进程/线程 补充进阶_第2张图片
Qt - 进程/线程 补充进阶_第3张图片

tdicethread 类

.h

#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

.cpp

#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)
}

widget 类

.h

#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

.cpp

#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();
}


QThread 多线程 第二种创建方法

创建

工作线程类必须继承自QObject,而不是QThread
工作线程的实例对象不能添加父对象(Worker worker=new Worker,(不能是new Worker(this)))

可以把多个任务,move到同一个子线程中,但是多个任务不能同时执行

连接

方法1:可以直接将子线程的QThread::started信号,与工作任务类对象的&Task::doWork(槽)函数关联
方法2:见moveToThread例子2,主线程主动发送自定义的statring信号,与工作任务类对象的&Task::doWork(槽)函数关联

moveToThread 例子1

class1 类(工作线程)

.h
#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

.cpp
#include "class1.h"

class1::class1(QObject *parent)
    : QObject{parent}
{

}

void class1::doWork()
{
    qDebug()<<"class1 sub thread"<<QThread::currentThread();
}

widget 类(主线程)

.h
#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

.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(worker1,&class1::destroyed,thread1,&QThread::quit);

    thread1->start();
}

Widget::~Widget()
{
}


moveToThread 例子2

只将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实例变量的生存周期内代码片段会得到保护,自动进行互斥量的解锁加锁

QMutex / 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 例子

例子 – 伪代码

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 !!!

QWaitCondition / QReadWriteLock 例子

tdicethread 类
.h
#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
.cpp
#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();
    }
}


widget类
.h
#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

.cpp
#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,没有资源可用,阻塞等待

QSemaphore 例子

mythread 类
.h
#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

.cpp
#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;
}

widget 类
.h
#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

.cpp
#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 – 还没看

你可能感兴趣的:(qt,qt)