天天看點

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