一文帶你了解Qt多線程的實現(xiàn)方式
QThread的run方法
一個QThread對象管理一個線程,一般從QThread繼承一個自定義的類,并實現(xiàn)run方法,在run函數(shù)中實現(xiàn)線程需要完成的任務(wù)。QThread自身定義了started() 和finish()兩個信號,started() 信號是在開始執(zhí)行之前發(fā)射,finished()信號是在線程就要結(jié)束時發(fā)射。
class WorkerThread : public QThread
{
Q_OBJECT
void run() override {
// TODO
emit sigMsg(result);
}
signals:
void sigMsg(const QString &s);
};
void main()
{
WorkerThread *workerThread = new WorkerThread(this);
workerThread->start();
}
特點
1、優(yōu)點:可以通過信號槽與外界進行通信。
2、缺點:每次新建一個線程都需要繼承QThread,實現(xiàn)一個新類,使用不太方便。
要自己進行資源管理,線程釋放和刪除。并且頻繁的創(chuàng)建和釋放會帶來比較大的內(nèi)存開銷。
3、適用場景:QThread適用于那些常駐內(nèi)存的任務(wù)。
QObject的moveToThread
創(chuàng)建一個繼承QObject的類MyThread,把要執(zhí)行的計算放到一個函數(shù)中doWork,然后new一個Qthread,并把創(chuàng)建的myThread類movetothread到創(chuàng)建好的子線程中,然后start子線程,這樣就實現(xiàn)了一個子線程。這里一定要通過信號去調(diào)用doWork函數(shù)。
class MyThread:public QObject
{
Q_OBJECT
public slots:
void doWork(){
int i=0;
while(i<4){
// ToDo
qDebug()<<QThread::currentThread()<<" "<<i++;
}
}
?
?
};
?
class MyTest :public QObject
{
Q_OBJECT
QThread workerThread;
public:
MyTest(){
MyThread* myThread = new MyThread(); // 這個任務(wù)函數(shù)不能有父對象,有父對象時不可以moveToThread
myThread->moveToThread(&workerThread);
workerThread.start();
connect(this,&MyTest::active,myThread,&MyThread::doWork); // 一定要通過槽函數(shù)去調(diào)用對應(yīng)的函數(shù),否則還是在主線程
}
~MyTest(){
workerThread.quit();
}
signals:
void active();
};
?
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
qDebug()<<QThread::currentThread();
ui->setupUi(this);
MyTest* t = new MyTest();
t->active();
QThread::sleep(3000);
t->deleteLater();
}
特點
一定要通過槽函數(shù)的形式去調(diào)用函數(shù),要注意!你創(chuàng)建的QThread對象實例,仍然存活在主線程上,而非子線程。所以如果你直接調(diào)用其中的函數(shù),那么還是在主線程上運行的。該方法并不是線程安全的。
QRunnalble的run
繼承Qrunnable,并重寫run虛函數(shù),使用QThreadPool啟動線程
class Runnable:public QRunnable
{
public:
Runnable();
~Runnable();
void run();
};
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);
returna.exec();
}
特點
1,無需手動釋放資源,QThreadPool啟動線程執(zhí)行完成后會自動釋放。
2,不能使用信號槽與外界通信。
3,QRunnable適用于線程任務(wù)量比較大,需要頻繁創(chuàng)建線程。QRunnable能有效減少內(nèi)存開銷。
QtConcurrent的run
使用QtConcurrent編寫的程序會根據(jù)可用的處理器內(nèi)核數(shù)自動調(diào)整使用的線程數(shù)。QtConcurrent::run能夠方便快捷的將任務(wù)丟到子線程中去執(zhí)行,無需繼承任何類,也不需要重寫函數(shù),使用非常簡單。通過QtConcurrent::run()返回的QFuture不支持取消、暫停,返回的QFuture只能用于查詢函數(shù)的運行/完成狀態(tài)和返回值。
函數(shù)原型:
QFuture<T> QtConcurrent::run(Function function, ...) QFuture<T> QtConcurrent::run(QThreadPool *pool, Function function, ...)
使用方式
QtConcurrent::run([=]() {
// TODO
});
線程同步
基于QMutex互斥同步
QMutex的目的是保護一個對象、數(shù)據(jù)結(jié)構(gòu)或者代碼段,所以同一時間只有一個線程可以訪問它。如果使用Mutex鎖那么多個線程在訪問一段代碼的時候是存在阻塞的,一個執(zhí)行完畢下一個線程才會繼續(xù)執(zhí)行
lock():試圖鎖定互斥量。如果另一個線程已經(jīng)鎖定這個互斥量,那么這次調(diào)用將阻塞直到那個線程把它解鎖。
unlock():進行解鎖
tryLock():試圖鎖定互斥量。如果鎖被得到,這個函數(shù)返回真。如果另一個進程已經(jīng)鎖定了這個互斥量,這個函數(shù)返回假,而不是一直等到這個鎖可用為止
QMutex mutex;
void DebugInfo()
{
mutex.lock();
qDebug("ABC");
qDebug("DEF");
mutex.unlock();
}
基于QReadWriteLock的線程同步
一種讀寫鎖,用于保護可以進行讀寫訪問的資源。這種索允許多個線程同時進行只讀訪問,但是一旦一個線程想要寫入資源,則必須阻止所有其他線程,直到寫入完成。
QReadWriteLock lock;
void ReaderThread::run()
{
...
lock.lockForRead();
read_file();
lock.unlock();
...
}
void WriterThread::run()
{
...
lock.lockForWrite();
write_file();
lock.unlock();
...
}
void lockForRead():鎖定讀取鎖。如果另一個線程已鎖定以進行寫入,則此函數(shù)將阻塞當前線程。如果線程已經(jīng)鎖定寫入,則無法鎖定讀取。void lockForWrite():鎖定寫入鎖。如果另一個線程(包括當前線程)已鎖定讀取或?qū)懭耄瑒t此函數(shù)將阻塞當前線程。如果線程已經(jīng)為讀取而鎖定,則不會為寫入而鎖定。
基于QWaitCondition的線程同步
QWaitCondition 允許線程在某些情況發(fā)生時喚醒另外的線程。一個或多個線程可以阻塞等待一QWaitCondition ,用wakeOne()或wakeAll()設(shè)置一個條件。wakeOne()隨機喚醒一個,wakeAll()喚醒所有
const int DataSize = 100000;
const int BufferSize = 8192;
char buffer[BufferSize];
QWaitCondition bufferNotEmpty;
QWaitCondition bufferNotFull;
QMutex mutex;
int numUsedBytes = 0;
class Producer : public QThread
{
public:
void run();
};
void Producer::run()
{
qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
for (int i = 0; i < DataSize; ++i) {
mutex.lock();
if (numUsedBytes == BufferSize)
bufferNotFull.wait(&mutex);
mutex.unlock();
buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];
mutex.lock();
++numUsedBytes;
bufferNotEmpty.wakeAll();
mutex.unlock();
}
}
class Consumer : public QThread
{
public:
void run();
};
void Consumer::run()
{
for (int i = 0; i < DataSize; ++i) {
mutex.lock();
if (numUsedBytes == 0)
bufferNotEmpty.wait(&mutex);
mutex.unlock();
fprintf(stderr, "%c", buffer[i % BufferSize]);
mutex.lock();
--numUsedBytes;
bufferNotFull.wakeAll();
mutex.unlock();
}
fprintf(stderr, "\n");
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
Producer producer;
Consumer consumer;
producer.start();
consumer.start();
producer.wait();
consumer.wait();
return 0;
}
到此這篇關(guān)于一文帶你了解Qt多線程的實現(xiàn)方式的文章就介紹到這了,更多相關(guān)Qt多線程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Visual Studio 2010/2013編譯V8引擎步驟分享
這篇文章主要介紹了使用Visual Studio 2013編譯V8引擎步驟分享,需要的朋友可以參考下2015-08-08
設(shè)計模式中的備忘錄模式解析及相關(guān)C++實例應(yīng)用
這篇文章主要介紹了設(shè)計模式中的備忘錄模式解析及相關(guān)C++實例應(yīng)用,備忘錄模式也經(jīng)常被用來在命令模式中維護可以撤銷(Undo)操作的狀態(tài),需要的朋友可以參考下2016-03-03

