Qt線程池


說明

Qt中可以有多種使用線程的方式:

  1. 繼承 QThread,重寫 run() 接口;
  2. 使用 moveToThread() 方法將 QObject 子類移至線程中,內部的所有使用信號槽的槽函數均在線程中執行;
  3. 使用 QThreadPool 線程池,搭配 QRunnable
  4. 使用 QtConcurrent

本文跳過第1和第2中方式,介紹后面兩種

線程池

  • 創建和銷毀線程需要和OS交互,少量線程影響不大,但是線程數量太大,勢必會影響性能,使用線程池可以這種開銷;
  • 線程池維護一定數量的線程,使用時,將指定函數傳遞給線程池,線程池會在線程中執行任務;

(一)QThreadPool和QRunnable

Qt中需要繼承 QRunnable,重寫 run() 方法,並,將其傳遞給線程池 QThreadPool 進行管理

QRunnable常用接口

bool QRunnable::autoDelete() const;
void QRunnable::setAutoDelete(bool autoDelete);
  • QRunnable 常用函數不多,主要設置其傳到底給線程池后,是否需要自動析構;
  • 若該值為false,則需要程序員手動析構,要注意內存泄漏;

QThreadPool常用接口

void QThreadPool::start(QRunnable * runnable, int priority = 0);
bool QThreadPool::tryStart(QRunnable * runnable);
  • start() 預定一個線程用於執行QRunnable接口,當預定的線程數量超出線程池的最大線程數后,QRunnable接口將會進入隊列,等有空閑線程后,再執行;
  • priority指定優先級
  • tryStart()start() 的不同之處在於,當沒有空閑線程后,不進入隊列,返回false
void QThreadPool::cancel(QRunnable * runnable);
void QThreadPool::clear();
  1. 如果,指定的QRunnable還沒有執行,則從隊列中移除
  2. 清空隊列中還沒有執行的QRunnable;
bool QThreadPool::waitForDone(int msecs = -1);
  • 等待所有線程結束並釋放資源(如果需要自動釋放的話);
  • msecs指定超時;
  • 若所有線程都被移除,則,返回true,否則返回false;
int	maxThreadCount() const
void setMaxThreadCount(int maxThreadCount)
  • 線程池維護的最大線程數量;
  • 設定該值,不會影響已經開始的線程;
  • 若沒有設定,默認值是最大線程數,可以用:QThread::idealThreadCount(); 獲取;
int	expiryTimeout() const
void setExpiryTimeout(int expiryTimeout)
  • 線程的終結超時;
  • 沒有開啟,且超過終結時間的線程,會退出,這些線程會根據需要重啟開始,即這些線程不會消失,線程池會重新取出這些線程,開啟或者放入隊列,所謂的終結超時就是重新排列等待隊列;
  • 建議在創建線程池后,調用 start() 前設定終結超時;
static QThreadPool * QThreadPool::globalInstance();
  • 全局內存池實例;
  • 若創建QThreadPool實例,則在實例生存周期內,內存池有效,

代碼示例

//MyRunnable.h
class MyRunnable : public QRunnable
{
public:
    MyRunnable(const QString& thread_name);
    void run();
private:
    QString threadName;
};
//MyRunnable.cpp
#include "myrunnable.h"
MyRunnable::MyRunnable(const QString &thread_name) : threadName(thread_name){}
void MyRunnable::run()
{
    qDebug()<<"Start thread id:"<<QThread::currentThreadId();
    int count = 0;
    while(true)
    {
        if(count >= 10)
        {
            break;
        }
        qDebug()<<threadName<<" Count:"<<count++;
        QThread::msleep(500);
    }
}
//調用處
MyRunnable* my_runnable = new MyRunnable("1# thread");
my_runnable->setAutoDelete(true);

MyRunnable* my_runnable_2 = new MyRunnable("2# thread");
my_runnable_2->setAutoDelete(true);

threadPool.start(my_runnable);
threadPool.start(my_runnable_2);

(二)QtConcurrent

若有大量工作需要完成,則使用方式1、2、3均可,但是若只有一小段工作,需要在線程中完成,無論是使用QThread,還是moveToThread,更或者,使用QThreadPool,都有大材小用的感覺,這時候,使用 QtConcurrent 就是最佳選擇

下面的說明,以Qt自帶的例子為基礎,並加入部分修改,例子目錄:..\Qt\Qt5.5.1_mingw\Examples\Qt-5.5\qtconcurrent\runfunction

pro文件中添加模塊

QT += concurrent

代碼示例:

QString hello(QString name)
{
    qDebug() << "Hello" << name << "from" << QThread::currentThread();
    return name;
}
//掉用處
QFuture<QString> f1 = QtConcurrent::run(hello, QString("Alice"));
QFuture<QString> f2 = QtConcurrent::run(&threadPool, hello, QString("Bob"));
f1.waitForFinished();
f2.waitForFinished();

qDebug()<<f1.result();
qDebug()<<f2.result();

輸出如下:

Hello "Alice" from QThread(0x1b1562a8, name = "Thread (pooled)")
Hello "Bob" from QThread(0x1b156248, name = "Thread (pooled)")
"Alice"
"Bob"

(1)說明:

QFuture<T> QtConcurrent::run(Function function, ...);
//QtConcurrent::run(QThreadPool::globalInstance(), function, ...);
QFuture<T> QtConcurrent::run(QThreadPool * pool, Function function, ...);

QtConcurrent::run() 方法的第一個參數是線程池,可以指定線程池,若沒有指定,則使用全局線程池;

(2)執行普通函數,並傳參,獲取返回值

參考上例

  • 將需要傳遞的參數依次跟在函數名后面;
  • 使用 QFuture<T>result() 方法獲取返回值;

(3)執行非const成員函數

QFuture<QString> f1 = QtConcurrent::run(this, &MainWindow::Hello, QString("Alice"));
QFuture<QString> f2 = QtConcurrent::run(&threadPool, this, &MainWindow::Hello, QString("Bob"));
  • 線程池和傳參以及返回值相同;
  • 既然是成員,則需要指定實例,且是非const,則可能需要修改成員,在函數名前傳入實例指針或者實例的應用;

(4)執行const成員

// call 'QList<QByteArray>  QByteArray::split(char sep) const' in a separate thread
QByteArray bytearray = "hello world";
QFuture<QList<QByteArray> > future = QtConcurrent::run(bytearray, &QByteArray::split, ',');
...
QList<QByteArray> result = future.result();

const成員不會使用非const成員,所以,傳入指針/引用或或者形參並沒有區別,上例中,就是傳入了形參


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM