Qt5多线程/线程池技术集锦(1)

简介: Qt5多线程/线程池技术集锦

1、用QObject的方法实现多线程


Qt有两种多线程的方法,其中一种是继承QThread的run函数,另外一种是把一个继承于QObject的类转移到一个Thread里。 Qt4.8之前都是使用继承QThread的run这种方法,但是Qt4.8之后,Qt官方建议使用第二种方法。


第二种方法用QObject来实现多线程有个非常好的优点,就是默认就支持事件循环(Qt的许多非GUI类也需要事件循环支持,如QTimer、QTcpSocket),QThread要支持事件循环需要在QThread::run()中调用QThread::exec()来提供对消息循环的支持,否则那些需要事件循环支持的类都不能正常发送信号,因此如果要使用信号和槽,那就直接使用QObject来实现多线程。


https://doc.qt.io/qt-5/threads.html


https://wiki.qt.io/QThreads_general_usage


https://wiki.qt.io/Threads_Events_QObjects



2、用QtConcurrent::run的方法实现多线程


https://doc.qt.io/qt-5/qtconcurrentrun.html


https://www.coologic.cn/2017/12/608/


2.1 基本的使用方式,注意线程id的获取方式是QThread::currentThreadId();


QString id = QString::asprintf("[%d]", QThread::currentThreadId());


#include <QCoreApplication>
#include <QDebug>
#include <QList>
#include <QThread>
#include <QtConcurrent>
void function(const QList<int> &param1, const int &param2, Qt::HANDLE main_id)
{
    qDebug() << "function param:" << param1 << param2 << main_id;
    qDebug() << "function thread id:" << QThread::currentThreadId();
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QList<int> testVactor;
    for (int i = 1; i <= 3; i++)
    {
        testVactor.push_back(i);
    }
    qDebug() << "main thread id:" << QThread::currentThreadId();
    QFuture<void> f = QtConcurrent::run(function, testVactor, 666, QThread::currentThreadId());
    f.waitForFinished(); //要等待,否则线程没运行完程序结束会出错
    return 0;
}

2.2 指定线程池的使用方式


有时候希望运行的函数在全局线程池或者局部线程池运行,而不是有qt托管处理,可以进行如下方式调用:


extern void aFunction();

QThreadPool pool;

QFuture<void> future = QtConcurrent::run(&pool, aFunction);

QtConcurrent::run和线程池组合使用的源码案例:

#include <QCoreApplication>
// Qt includes
#include <QtConcurrent>
#include <QtCore>
#include <QtGui>
#include <QtNetwork>
#if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
#include <QtWidgets>
#endif
QThreadPool m_threadpool;
void func(void)
{
    qDebug() << "hello";
    QFuture<void> f4 = QtConcurrent::run(&m_threadpool, [&](void) {
        for (int i = 1000; i < 1010; i++)
        {
            qDebug() << i;
            Sleep(1000);
        }
    });
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    //初始化线程池
    int cpu = std::thread::hardware_concurrency();
    qDebug() << "cpu:" << cpu;
    m_threadpool.setMaxThreadCount(cpu);
    m_threadpool.setExpiryTimeout(-1);
    QFuture<void> f1 = QtConcurrent::run(&m_threadpool, [&](void) {
        for (int i = 0; i < 10; i++)
        {
            qDebug() << i;
            Sleep(1000);
        }
    });
    QFuture<void> f2 = QtConcurrent::run(&m_threadpool, [&](void) {
        for (int i = 100; i < 110; i++)
        {
            qDebug() << i;
            Sleep(1000);
        }
    });
    QFuture<void> f3 = QtConcurrent::run(func);
    while (true)
    {
        ;
    }
    return a.exec();
}

2.3 使用QFutureWatcher实现进度条的例子


https://wiki.qt.io/Progress_Bar


\Qt5.12.9\Examples\Qt-5.12.9\qtconcurrent\progressdialog


长时间进行操作时,通常需要显示进度条。 在某些耗时的运算中,我们没有方法来实时跟踪操作的进度,所知的只是运算完成时。 为了实现进度条,您可以使用progressBar小部件,然后在另一个线程中运行该操作(使用moveToThread)。 这通常需要创建一个特殊的对象(QObject的子类,该对象运行操作,然后发出finish信号),如果您需要对许多不同的操作执行此操作,则可能会很麻烦。但是,使用QFutureWatcher和QtConcurrent::run(),这非常容易。 这篇官方文档演示如何将这种技术与QProgressDialog和QProgressBar一起使用。


如果QProgressBar最小值和最大值都设置为0,进度条会显示一个繁忙指示,而不会显示当前的值。



3、线程池


3.1 全局线程池

QThreadPool提供了一个静态成员函数,QThreadPool *  globalInstance(),使用此方法可获取一个当前进程的全局线程池,可在多个类中共同使用一个线程池。


3.2 局部线程池

和常规类的使用相同,可以通过QThreadPool pool;类的实例化方式建立一个局部线程池,并由当前类维护,可保证此线程池仅供当前类应用。


3.3 QThreadPool启动的线程,需要从QRunnable类继承。需要实现QRunnable类的run虚函数。QRunnable的autoDelete默认返回true,若需要更改需要调用setAutoDelete进行更改。QRunnable只有run、autodelete、setautodelete这三个关键函数。


3.4 主要的几个函数


int activeThreadCount() const //当前的活动线程数量

void clear()//清除所有当前排队但未开始运行的任务

int expiryTimeout() const//线程长时间未使用将会自动退出节约资源,此函数返回等待时间。默认值30000ms。

int maxThreadCount() const//线程池可维护的最大线程数量。默认值CPU核心数*2。

void setExpiryTimeout(int expiryTimeout)//设置线程回收的等待时间;可以设置为-1,没有超时限制。

void setMaxThreadCount(int maxThreadCount)//设置最大线程数量

void start(QRunnable *runnable, int priority = 0)//加入一个运算到队列,注意start不一定立刻启动,只是插入到队列,排到了才会开始运行。需要传入QRunnable,priority是线程优先级。

bool waitForDone(int msecs = -1)//等待所有线程运行结束并退出,参数为等待时间,-1表示一直等待到最后一个线程退出

https://doc.qt.io/qt-5/qthreadpool.html


https://doc.qt.io/qt-5/qrunnable.html


https://www.cnblogs.com/techiel/p/8018379.html


相关文章
|
3天前
|
Java
线程池和线程详细教程
线程池和线程详细教程
|
5天前
|
设计模式 安全 Java
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
18 1
|
5天前
|
监控 Java 调度
Java面试题:描述Java线程池的概念、用途及常见的线程池类型。介绍一下Java中的线程池有哪些优缺点
Java面试题:描述Java线程池的概念、用途及常见的线程池类型。介绍一下Java中的线程池有哪些优缺点
18 1
|
5天前
|
设计模式 存储 安全
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
15 1
|
5天前
|
设计模式 安全 Java
Java面试题:请解释Java中的线程池以及为什么要使用线程池?请解释Java中的内存模型以及如何避免内存泄漏?请解释Java中的并发工具包以及如何实现一个简单的线程安全队列?
Java面试题:请解释Java中的线程池以及为什么要使用线程池?请解释Java中的内存模型以及如何避免内存泄漏?请解释Java中的并发工具包以及如何实现一个简单的线程安全队列?
10 1
|
5天前
|
设计模式 缓存 安全
Java面试题:工厂模式与内存泄漏防范?线程安全与volatile关键字的适用性?并发集合与线程池管理问题
Java面试题:工厂模式与内存泄漏防范?线程安全与volatile关键字的适用性?并发集合与线程池管理问题
12 1
|
3天前
|
缓存 Linux 编译器
【Linux】多线程——线程概念|进程VS线程|线程控制(下)
【Linux】多线程——线程概念|进程VS线程|线程控制(下)
11 0
|
3天前
|
存储 Linux 调度
【Linux】多线程——线程概念|进程VS线程|线程控制(上)
【Linux】多线程——线程概念|进程VS线程|线程控制(上)
14 0
|
5天前
|
设计模式 并行计算 安全
Java面试题:如何使用设计模式优化多线程环境下的资源管理?Java内存模型与并发工具类的协同工作,描述ForkJoinPool的工作机制,并解释其在并行计算中的优势。如何根据任务特性调整线程池参数
Java面试题:如何使用设计模式优化多线程环境下的资源管理?Java内存模型与并发工具类的协同工作,描述ForkJoinPool的工作机制,并解释其在并行计算中的优势。如何根据任务特性调整线程池参数
10 0
|
5天前
|
设计模式 安全 NoSQL
Java面试题:结合单例模式与Java内存管理,设计一个线程安全的单例类?分析Java多线程工具类ExecutorService与Java并发工具包中的工具类,设计一个Java并发框架的分布式锁实现
Java面试题:结合单例模式与Java内存管理,设计一个线程安全的单例类?分析Java多线程工具类ExecutorService与Java并发工具包中的工具类,设计一个Java并发框架的分布式锁实现
13 0