QT 多线程之线程池QThreadPool
QT多线程编程系列专栏文章共有12篇,全面的讲述、实现、运行了QT多线程的各种操作,包括运行原理、线程、进程、多线程、锁、QMutex、QSemaphore、 Emit、Sgnals、Slot、QWaitCondition、线程事件循环、QObjects、重入与线程安全、主线程子线程互相传值、线程同步与异步处理、线程的使用、浅拷贝、深拷贝、隐式共享、隐式共享机制对STL样式迭代器的影响等等文章。
在一个应用程序中,我们需要多次使用线程,也就意味着,我们需要多次创建并销毁线程。而创建并销毁线程的过程势必会消耗内存。而在日常开发中,内存资源是及其宝贵的,所以,我们这里就有了本篇文章QT 多线程之线程池QThreadPool。在程序逻辑中经常会碰到需要处理大批量任务的情况,比如密集的网络请求,或者日志分析等等。一般会创建一个队列,用一个或者多个线程去消费这个队列,一般也要处理队列的加锁和解锁的问题。
详解QThreadPool线程池过程中,给大家举栗出线程池优点和运行的注意事项以及示例Demo。
本文作者原创首发于CSDN,本文原创请勿转载
版权声明:本文为CSDN博主「双子座断点」的原创文章,遵循CC 4.0 BY-SA版权协议。
原文链接:https://blog.csdn.net/qq_37529913/article/details/110127940
1. 线程池的优点
- 创建和销毁线程需要和OS交互,少量线程影响不大,但是线程数量太大,势必会影响性能,使用线程池可以这种开销;
- 线程池维护一定数量的线程,使用时,将指定函数传递给线程池,线程池会在线程中执行任务;
2. QT线程池函数
int activeThreadCount() const //当前的活动线程数量
void clear()//清除所有当前排队但未开始运行的任务
int expiryTimeout() const//线程长时间未使用将会自动退出节约资源,此函数返回等待时间
int maxThreadCount() const//线程池可维护的最大线程数量
void releaseThread()//释放被保留的线程
void reserveThread()//保留线程,此线程将不会占用最大线程数量,从而可能会引起当前活动线程数量大于最大线程数量的情况
void setExpiryTimeout(int expiryTimeout)//设置线程回收的等待时间
void setMaxThreadCount(int maxThreadCount)//设置最大线程数量
void setStackSize(uint stackSize)//此属性包含线程池工作线程的堆栈大小。
uint stackSize() const//堆大小
void start(QRunnable *runnable, int priority = 0)//加入一个运算到队列,注意start不一定立刻启动,只是插入到队列,排到了才会开始运行。需要传入QRunnable ,后续介绍
bool tryStart(QRunnable *runnable)//尝试启动一个
bool tryTake(QRunnable *runnable)//删除队列中的一个QRunnable,若当前QRunnable 未启动则返回成功,正在运行则返回失败
bool waitForDone(int?<i>msecs</i>?=?-1)//等待所有线程运行结束并退出,参数为等待时间-1表示一直等待到最后一个线程退出
3. 程序基本界面
4. 类说明
QThreadPool类:
用来管理 QThreads,经过测试QThreadPool线程池函数并不是安全线程,多个线程操作还是会出现抢资源现象,同步还是需要互斥锁或者信号量来同步。
主要属性:
1、activeThreadCount: 此属性表示线程池中的活动线程数,通过activeThreadCount() 调用。
2、expiryTimeout: 线程活着的时间。没有设置expiryTimeout毫秒的线程会自动退出,此类线程将根据需要重新启动。默认的expiryTimeout为30000毫秒 (30 秒)。如果expiryTimeout为负, 则新创建的线程将不会过期, 在线程池被销毁之前, 它们将不会退出。通过expiryTimeout()调用,通setExpiryTimeout(int expiryTimeout)设置 。
3、maxThreadCount : int 表示线程池使用的最大线程数。
通过maxThreadCount() 调用,通过setMaxThreadCount(int maxThreadCount) 设置
注意:即使maxThreadCount限制为零或为负数, 线程池也至少有1个线程。
主要成员函数
QThreadPool *QThreadPool::globalInstance()
返回Qt应用程序全局线程池实例。
void reserveThread()
预约一个线程,这个函数总是会增加活动线程的数量。这意味着通过使用这个函数,activeThreadCount()可以返回一个大于maxThreadCount()的值。
void releaseThread()
释放以前通过调用reserveThread()预约的线程。
如果不先预约一个线程,调用这个函数会临时增加maxThreadCount()。当线程进入休眠等待时,能够允许其他线程继续。
要记得在完成等待时调用reserveThread(),以便线程池可以正确控制activeThreadCount()。
void QThreadPool :: start(QRunnable * runnable,int priority = 0)
在任务数量小于maxThreadCount时,为每个runnable任务预约一个线程。超过maxThreadCount时,将任务放入运行队列中。priority 参数用来设置线程运行优先级。
bool tryStart(QRunnable *runnable)
此方法尝试预约一个线程来运行runnable。
如果在调用的时候没有线程可用,那么这个函数什么都不做,并返回false。否则,将使用一个可用线程立即运行runnable,并返回此函数true。
void clear()
用于删除在任务队列中,还没有启动的任务。
bool tryTake(QRunnable *runnable)
如果runnable任务还没开始运行,那么从队列中删除此runable任务,此时函数返回true;如果runnable任务已经运行,返回false。
只用来删除runnable->autoDelete() == false的runnable任务,否则可能会删错任务.
bool waitForDone(int msecs = -1)
等待msecs毫秒, 以便所有线程退出并从线程池中移除所有线程。如果删除了所有线程, 则返回true ,否则, 它将返回false。默认等待时间为-1,即等待最后一个线程退出。
内容出自: https://blog.csdn.net/y396397735/article/details/78637634
QRunnable类:
是所有runable对象的基类。
QRunnable类是一个接口, 用于表示需要执行的任务或代码段, 具体任务在run() 函数内部实现。
可以使用QThreadPool在各个独立的线程中执行代码。如果autoDelete() 返回true (默认值), QThreadPool将自动删除QRunnable 。使用setAutoDelete() 可更改是否自动删除。
QThreadPool 是创建线程池函数,QRunnable是线程池的线程具体执行操作函数。两者要搭配使用。
主要成员函数
bool autoDelete() const
获取自动删除是否启用,启用返回true,未启用返回false。
virtual void run() = 0
纯虚函数,在QRunnable子类中实现详细任务处理逻辑。
void setAutoDelete(bool autoDelete)
如果autoDelete为 true, 则启用自动删除。否则自动删除将被禁用。
如果启用了自动删除, QThreadPool将在调用 run () 函数返回后自动删除此runable对象。否则, runable对象所有权不属于线程池,由开发人员管理。
请注意, 必须先设置此标志,(默认构造函数已经将其设置为true),然后才能调用QThreadPool:: start()。在QThreadPool:: start() 之后调用此函数将导致不可预测后果。
内容出自:https://blog.csdn.net/y396397735/article/details/78637634
5. 示例Demo
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QRunnable>
#include <QObject>
#include <QDebug>
#include <QReadWriteLock>
#include <QTime>
#include <QSemaphore>
class mythread : public QObject, public QRunnable
{
Q_OBJECT
public:
mythread();
//QThread的虚函数
//线程处理函数
//不能直接调用,通过start()间接调用
void run();
signals:
void isDone(int); //处理完成信号
void mySignal(); //注意!要使用信号,采用QObejct 和 QRunnable多继承,记得QObject要放在前面
public slots:
//接收主线程的消息
void recMegFromMain(QString);
};
class mythread1 : public QObject, public QRunnable
{
Q_OBJECT
public:
void run();
};
class mythread2 : public QObject, public QRunnable
{
Q_OBJECT
public:
void run();
};
class mythread3 : public QObject, public QRunnable
{
Q_OBJECT
public:
void run();
};
class mythread4 : public QObject, public QRunnable
{
Q_OBJECT
public:
void run();
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
#include <QMutex>
#include <QRandomGenerator>
#include <QWaitCondition>
#include <QThread>
/*
如果乱码就加上QStringLiteral();
#pragma execution_character_set("GB2312")
*/
mythread::mythread()
{
}
void mythread::run()
{
for(int i=0;i<10;i++){
//QThread::sleep(2);
qDebug()<< QStringLiteral("PrintTask run 被调用,调用线程ID为:") << QThread::currentThread() << QStringLiteral( " 线程1打印数据:") <<QString::number(i);
}
//emit isDone(1); //发送完成信号
}
void mythread::recMegFromMain(QString str)
{
qDebug()<< "子线程接收到" <<str;
}
void mythread1::run()
{
qDebug()<< QStringLiteral("PrintTask run 被调用,调用线程ID为:") << QThread::currentThread() ;
for(int i=10;i<20;i++){
QThread::sleep(2);
qDebug()<< "线程2打印数据:" <<QString::number(i);
}
//emit isDone(1); //发送完成信号
}
void mythread2::run()
{
qDebug()<< "PrintTask run 被调用,调用线程ID为:" << QThread::currentThread() ;
for(int i=20;i<30;i++){
QThread::sleep(2);
qDebug()<< "线程3打印数据:" <<QString::number(i);
}
//emit isDone(1); //发送完成信号
}
void mythread3::run()
{
qDebug()<< "PrintTask run 被调用,调用线程ID为:" << QThread::currentThread() ;
for(int i=30;i<40;i++){
QThread::sleep(2);
qDebug()<< "线程4打印数据:" <<QString::number(i);
}
//emit isDone(1); //发送完成信号
}
void mythread4::run()
{
qDebug()<< "PrintTask run 被调用,调用线程ID为:" << QThread::currentThread() ;
for(int i=40;i<50;i++){
QThread::sleep(2);
qDebug()<< "线程5打印数据:" <<QString::number(i);
}
//emit isDone(1); //发送完成信号
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "mythread.h"
#include <QDebug>
#include <QMessageBox>
#include <QThreadPool>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void dealDone(); //线程槽函数
void mySlot();
void receiveMsgFromThread(int);
void sengMsgToThreadBtn();
private:
Ui::MainWindow *ui;
QThreadPool pool;
mythread* task = new mythread();
mythread1* task1 = new mythread1();
mythread2* task2 = new mythread2();
mythread3* task3 = new mythread3();
mythread4* task4 = new mythread4();
signals:
//给子线程发消息
void sengMsgToThread(QString);
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
void on_pushButton_4_clicked();
void on_pushButton_5_clicked();
void on_pushButton_6_clicked();
void on_pushButton_7_clicked();
void on_pushButton_8_clicked();
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QReadWriteLock>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//设置最大线程数为3的一个线程池
pool.setMaxThreadCount(5);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::sengMsgToThreadBtn()
{
emit sengMsgToThread("hello");
}
// 定义槽函数 mySlot()
void MainWindow::mySlot()
{
QMessageBox::about(this,"Tsignal", "响应线程中的mySlot函数");
}
//接收线程函数
void MainWindow::receiveMsgFromThread(int i)
{
QString str = QString::number(i);
qDebug()<<str;
}
void MainWindow::dealDone()
{
ui->label->setText("线程停止");
//停止线程
}
void MainWindow::on_pushButton_clicked()
{
//QThread::sleep(1);
//pool.waitForDone(); //等待任务结束
on_pushButton_4_clicked();
on_pushButton_5_clicked();
on_pushButton_6_clicked();
on_pushButton_7_clicked();
on_pushButton_8_clicked();
//启动线程,处理数据
ui->label->setText("start");
}
void MainWindow::on_pushButton_2_clicked()
{
//pool.releaseThread();
//停止线程
//dealDone();
//sengMsgToThreadBtn();
}
void MainWindow::on_pushButton_3_clicked()
{
qDebug() <<pool.activeThreadCount();
//sengMsgToThreadBtn();
}
void MainWindow::on_pushButton_4_clicked()
{
pool.start(task); //任务放进线程池
task->setAutoDelete(false);
}
void MainWindow::on_pushButton_5_clicked()
{
pool.start(task); //任务放进线程池
task->setAutoDelete(false);
/*
pool.start(task1);
task1->setAutoDelete(false);
*/
}
void MainWindow::on_pushButton_6_clicked()
{
pool.start(task); //任务放进线程池
//pool.start(task2);
}
void MainWindow::on_pushButton_7_clicked()
{
pool.start(task); //任务放进线程池
//pool.start(task3);
}
void MainWindow::on_pushButton_8_clicked()
{
pool.start(task); //任务放进线程池
//pool.start(task4);
}
这个已经是比较完整的项目了,下载地址:QT_ThreadPool.rar_qt线程池-C++文档类资源-CSDN下载
6. 其它线程文章
以下文章均为作者原创文章,看完记得收藏、关注加👍
QT 初识线程(简单实现):https://blog.csdn.net/qq_37529913/article/details/110127940
QT QMutex使用详解:https://blog.csdn.net/qq_37529913/article/details/110187452
QT 线程之QSemaphore(深入理解):https://blog.csdn.net/qq_37529913/article/details/110187121
QT线程 Emit、Sgnals、Slot详细解释:https://blog.csdn.net/qq_37529913/article/details/110211435
QT 线程之QWaitCondition(深入理解):https://blog.csdn.net/qq_37529913/article/details/110212704
Qt 多线程之线程事件循环(深入理解):https://blog.csdn.net/qq_37529913/article/details/110229382
QT线程之QObjects(深入理解):https://blog.csdn.net/qq_37529913/article/details/110228837
QT线程之可重入与线程安全(深入理解):https://blog.csdn.net/qq_37529913/article/details/110224166
QT 主线程子线程互相传值:QT 主线程子线程互相传值_双子座断点的博客-CSDN博客_qt主线程给子线程传数据
QT线程同步与异步处理:QT线程同步与异步处理_双子座断点的博客-CSDN博客_qt异步线程
QT 多线程之线程池QThreadPool(深入理解):QT 多线程之线程池QThreadPool(深入理解)_双子座断点的博客-CSDN博客_qthreadpool
QT之浅拷贝、深拷贝、隐式共享(深度理解必看文章):https://blog.csdn.net/qq_37529913/article/details/110235596
QT 隐式共享机制对STL样式迭代器的影响:https://blog.csdn.net/qq_37529913/article/details/110252454
- 点赞
- 收藏
- 关注作者
评论(0)