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

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


上一篇:Java多线程设计模式(5)Future模式


下一篇:Postman:Postman简介、安装、入门使用方法详细攻略(二)