看了不少Qt線程的東西,下面總結一下Qt新建一個線程的方法。
一、繼承QThread
繼承QThread,這應該是最常用的方法了。我們可以通過重寫虛函數void QThread::run ()實現我們自己想做的操作,實現新建線程的目的。前面已經介紹了Qthread,這里就不重復了。
這種方法,我們每一次要新建一個線程都需要繼承Qthread,實現一個新的類,有點不太方便。但是相對於Qrunnable,這種方法的好處就是我們可以直接調用對象的start()函數啟動線程,而Qrunnable必須借助QthreadPool。
二、繼承QRunnable
Qrunnable是所有可執行對象的基類。我們可以繼承Qrunnable,並重寫虛函數void QRunnable::run () 。我們可以用QThreadPool讓我們的一個QRunnable對象在另外的線程中運行,如果autoDelete()返回true(默認),那么QThreadPool將會在run()運行結束后自動刪除Qrunnable對象。可以調用void QRunnable::setAutoDelete ( bool autoDelete )更改auto-deletion標記。需要注意的是,必須在調用QThreadPool::start()之前設置,在調用QThreadPool::start()之后設置的結果是未定義的。
下面是我寫的簡單的例子:
class Runnable:publicQRunnable { //Q_OBJECT 注意了,Qrunnable不是QObject的子類。 public: Runnable(); ~Runnable(); void run(); protected: private: }; Runnable::Runnable():QRunnable() { } Runnable::~Runnable() { cout<<"~Runnable()"<<endl; } void Runnable::run() { cout<<"Runnable::run()thread :"<<QThread::currentThreadId()<<endl; cout<<"dosomething ...."<<endl; } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); cout<<"mainthread :"<<QThread::currentThreadId()<<endl; Runnable runObj; QThreadPool::globalInstance()->start(&runObj); return.exec(); }
由結果可看出,run()確實是在不同於主線程的另外線程中運行的,而且在運行結束后就調用了析構函數,因為默認是可以自動被銷毀的。
我們可以對同一個對象多次調用QThreadPool::start(),如果是可以自動被銷毀的,Qrunnable對象會在最后一個線程離開了run函數之后才被銷毀的。
int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); cout<<"mainthread :"<<QThread::currentThreadId()<<endl; Runnable runObj; QThreadPool::globalInstance()->start(&runObj); QThreadPool::globalInstance()->start(&runObj); QThreadPool::globalInstance()->start(&runObj); returna.exec(); }
我三次調用QThreadPool::globalInstance()->start(&runObj);,但是在三次都執行完之后才運行析構函數。

這種新建線程的方法的最大的缺點就是:不能使用Qt的信號—槽機制,因為Qrunnable不是繼承自QObject。所以我們要想知道線程是否運行結束或獲取運行結果可能會比較麻煩。還有就是我們不能直接調用run()啟動線程,必須借助於QthreadPool。
但是這種方法的好處就是,可以讓QThreadPool來管理線程,QThreadPool會自動的清理我們新建的Qrunnable對象。
三、使用moveToThread
首先我們必須實現繼承QObject的一個類,實現我們想要的功能。
class Worker:publicQObject { Q_OBJECT public: Worker(); ~Worker(); protected slots: voidfun1(); void fun2(); private: }; Worker::Worker():QObject() { } Worker::~Worker() { } void Worker::fun1() { cout<<"Worker::fun1() thread : "<<QThread::currentThreadId()<<endl; }
接着創建一個對象,並調用:moveToThread ( QThread * targetThread ),讓對象在新的線程中運行。
int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); cout<<"mainthread :"<<QThread::currentThreadId()<<endl; QThread thread; Worker work; thread.start(); //注意記得啟動線程 work.moveToThread(&thread); //由於不能直接調用worker //的函數,所以一般用信號觸發調用 QTimer::singleShot(0,&work,SLOT(fun1())); QTimer::singleShot(0,&work,SLOT(fun2())); returna.exec(); }
這樣就能讓fun1()和fun2()都運行在thread線程中了。
需要注意的是:在work 的函數結束運行前,thread不能被析構。Thread的生命期不能小於work。否則的話程序就好崩掉了。
像下面的代碼肯定是不行的。
void Dialog::startWork() { QThread thread; Worker*work = new Worker; thread.start(); work->moveToThread(&thread); QTimer::singleShot(0,work,SLOT(fun1())); QTimer::singleShot(0,work,SLOT(fun2())); }
所以thread 必須是new出來的。但是這樣的話,就感覺有點麻煩,我們要同時管理thread和work,因為都是new 出來,我們需要負責清理。為了避免這樣的麻煩,我想到的方法是,在work類中添加一個QThread成員。
class Worker:publicQObject { Q_OBJECT public: Worker(); ~Worker(); protectedslots: voidfun1(); voidfun2(); private: QThread m_thread; }; Worker::Worker():QObject() { m_thread.start(); this->moveToThread(&m_thread); }
這樣我們在用的時候只需要newwork就行了。
四、使用QtConcurrent::run
其實前面也有用到QtConcurrent::run啟動新線程了。QtConcurrent命名空間提供了很多方法可以實現並發編程,這個以后再深入探討了,這里只是大概講一下啟動線程。還是用上面的worker代碼作為例子:
void Worker::start() { QtConcurrent::run(this,&Worker::fun1); QtConcurrent::run(this,&Worker::fun2); } QtConcurrent::run是個模板函數,有很多種形式,我們也可以讓全局的函數允許在另外的線程中。 void printMes(char*mes) { cout<<"pprintMes(char*mes) thread : "<<QThread::currentThreadId()<<endl; cout<<mes<<endl; } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); cout<<"mainthread :"<<QThread::currentThreadId()<<endl; char *mes= "hello world"; QtConcurrent::run(printMes,mes); returna.exec(); }
目前所知的新建線程的方法就大概這些了,希望對大家有用,可能還要別的,以后再繼續學習了
