您好,登錄后才能下訂單哦!
我們之前的程序都是單線程運行,接下來我們開始引入多線程。就相當于以前的一個人在工作,現在多個人一起工作。
Qt中非常有必要使用多線程,這是因為,Qt應用是事件驅動型的,一旦某個事件處理函數處理時間過久,就會造成其它的事件得不到及時處理。
Qt中使用QThread來管理線程,一個QThread對象,就是一個線程。QThread對象也有消息循序exec()函數,用來處理自己這個線程的事件。
1、Qt第一種創建線程方式
首先要繼承QThread
重寫虛函數QThread::run
[virtual protected] void QThread::run() /* * 基類QThread的run函數只是簡單啟動exec()消息循環 */
例如:
#include <QApplication> #include <QThread> #include <QDebug> class MyThread : public QThread { public: void run() { qDebug() << "QThread begin" << endl; qDebug() << "child thread" << QThread::currentThreadId() << endl; QThread::sleep(5); qDebug() << "QThread end" << endl; exec(); } }; int main(int argc, char** argv) { QApplication app(argc, argv); MyThread thread; thread.start(); qDebug() << "main thread" << QThread::currentThreadId() << endl; QThread::sleep(5); qDebug() << "main thread" << QThread::currentThreadId() << endl; thread.quit(); qDebug() << "main thread thread.quit()" << endl; tread.wait(); qDebug() << "main thread thread.wait()" << endl; return app.exec(); }
使用QThread的quit可以退出線程的消息循環,有時候不是馬上退出,需要等到cpu的控制權交還給線程的exec()。
一般在子線程退出的時候需要主線程去回收資源,可以調用QThread的wait,等待子線程的退出,然后回收資源.
繼承 QObject
實例化一個QThread對象
實現槽函數.
QObject子類對象通過moveToThread將自己放到線程QThread對象中.
調用QThread對象的start函數啟動線程
必須通過發射信號來讓槽函數在線程中執行,發射的信號存放在線程exec()消息隊列中。
例如:
mywork.h
#ifndef MYWORK_H #define MYWORK_H #include <QThread> #include <QDebug> class MyWork : public QObject { Q_OBJECT public slots: void workSlot() { qDebug() << "QThread begin" << endl; qDebug() << "child thread" << QThread::currentThreadId() << endl; QThread::sleep(5); qDebug() << "QThread end" << endl; } }; #endif // MYWORK_H
widget.cpp
#include <QApplication> #include <QThread> #include <QDebug> #include "mywork.h" int main(int argc, char** argv) { qDebug() << "main thread" << QThread::currentThreadId() << endl; QApplication app(argc, argv); QThread thread; MyWork work; work.moveToThread(&thread); QObject::connect(&thread, SIGNAL(started()), &work, SLOT(workSlot())); thread.start(); QThread::sleep(6); qDebug() << "thread is runing" << thread.isRunning() << endl; thread.quit(); //調用quit讓線程退出消息循環,否則線程一直在exec循環中 thread.wait(); //調用完quit后緊接著要調用wait來回收線程資源 qDebug() << "thread is runing" << thread.isRunning() << endl; return app.exec(); }
需要特別注意的是:
既然QObject對象無法托管內存對象,那么到底是先釋放線程對象,還是先釋放這個QObject對象?
先把QObject在線程循環中釋放(使用QObject::deleteLater函數),然后QThread::quit,然后QThread::wait。
例如:
mywork.h
#ifndef MYWORK_H #define MYWORK_H #include <QThread> #include <QDebug> class MyWork : public QObject { Q_OBJECT public: ~MyWork() { qDebug() << __FUNCTION__ << endl; } public slots: void workSlot() { while(1) { qDebug() << "Work begin" << endl; QThread::sleep(5); qDebug() << "work end" << endl; } } void otherWorkSlot() { qDebug() << "otherWork begin" << endl; QThread::sleep(5); qDebug() << "otherWork end" << endl; } }; #endif // MYWORK_H
widget.h
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include "mywork.h" class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = 0); ~Widget(); private: QThread *_thread; MyWork * _myWork; }; #endif // WIDGET_H
widget.cpp
#include "widget.h" #include <QPushButton> #include "mywork.h" #include <QThread> #include <QHBoxLayout> Widget::Widget(QWidget *parent) : QWidget(parent) { _myWork = new MyWork; _thread = new QThread(this); _myWork->moveToThread(_thread); _thread->start(); QPushButton *pb0 = new QPushButton("work", this); QPushButton *pb1 = new QPushButton("pb", this); QHBoxLayout *hBox = new QHBoxLayout(this); hBox->addWidget(pb0); hBox->addWidget(pb1); this->setLayout(hBox); /*發射信號給在另外一個線程的對象的隊列中*/ connect(pb0, SIGNAL(clicked()), _myWork, SLOT(workSlot())); connect(pb1, SIGNAL(clicked()), _myWork, SLOT(otherWorkSlot())); /*推薦用法釋放內存*/ //connect(_thread, SIGNAL(finished()), _myWork, SLOT(deleteLater())); } Widget::~Widget() { _myWork->deleteLater(); //一定要在QThread線程退出之前 _thread->quit(); _thread->wait(); }
多線程在訪問同時一個資源,(例如:多個線程可操作的變量、函數等),到底誰來使用這個資源是一個問題,就像一大群人去搶同一塊蛋糕,可能其中一個人搶到,更有可能蛋糕被搶個稀爛。在多線程中,這個叫作競爭冒險。那么我們需要定一個規則來約束每一個人,比如:每個人排隊來領蛋糕,這個在多線程中叫作同步方法。
需要注意的是,同步不是同時,而是有序進行。
Qt中的互斥鎖是QMutex,不繼承任何Qt基類,使用QMutex來鎖共享資源,哪個線程搶到鑰匙,哪個線程就有這個資源的使用權,其它線程等待這個線程使用完資源并歸還鑰匙,然后它們再去搶鑰匙。
例如:
QMutex mutex; //這對象一般定義在多個線程能訪問的地方 mutex.lock(); //多個線程調用這個函數去獲取鎖,沒有獲取到的線程,將阻塞等待在這個函數上。 mutex.unlock(); //釋放鎖
QMutex::lock函數會讓線程等待獲取鎖,如果不想等待,可以使用一下函數替換:
bool QMutex::tryLock(int timeout = 0) /* *參數 int timeout:等到timeout毫秒,不管有沒獲取到鎖都返回,timeout為0時,直接返回。 *返回值 true代表獲取到鎖,false沒有獲取到 */
有時候我們會忘記釋放鎖,Qt還為我們提供了管理鎖的類QMutexLocker
QMutex mutex; void func() { QMutexLocker locker(_mutex); //QMutexLocker最好實例化成棧對象,釋放之前將QMutex解鎖。 }
以上例子,一旦func()執行完成locker自動釋放,釋放之前先解鎖。
互斥鎖保護的資源同一時刻只能有一個線程能夠獲取使用權,有些資源是可以限定多個線程同時訪問,那么這個時候可以使用信號量。在Qt中信號量為QSemaphore。
QSemaphore sem(2) //初始化信號量為2 sem.acquire(); //信號量部位0的時候,調用這個函數會讓信號量-1,一旦信號量為零,阻塞等待 semaphore.release(); //使信號量+1
定時器可以隔一段時間發出信號,通過接收這個信號來處理一些定時任務,需要注意的是,定時器并沒有開啟一個新線程。Qt中的定時器是QTimer繼承自QObject。
通過QTimer::start()來啟動定時器
void start(int msec) /* *定時msec毫秒后,發射timeout()信號 */
通過鏈接信號timeout()來處理一些定時任務,例如:
#include "dialog.h" #include <QTimer> #include <qdebug.h> Dialog::Dialog(QWidget *parent) : QDialog(parent) { QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(doSomeThing())); timer->start(3000); } void Dialog::doSomeThing() { qDebug() << __FUNCTION__ << endl; } Dialog::~Dialog() { }
本文主要介紹了Qt多線程類QThread與Qt定時器類QTimer的詳細方法與實例,更多關于Qt開發知識請查看下面的相關鏈接
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。