




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1、Qt多線程概述Qt線程類Qt包含下面一些線程相關(guān)的類:QThread提供了開始一個新線程的方法QThreadStorage提供逐線程數(shù)據(jù)存儲QMutex提供相互排斥的鎖,或互斥量QMutexLocker是一個便利類,它可以自動對QMutex加鎖與解鎖QReadWriterLock提供了一個可以同時讀操作的鎖QReadLocker與QWriteLocker是便利類,它自動對QReadWriteLock加鎖與解鎖QSemaphore提供了一個整型信號量,是互斥量的泛化QWaitCondition提供了一種方法,使得線程可以在被另外線程喚醒之前一直休眠。Qt線程的創(chuàng)建Qt線程中有一個公共的抽象類,所
2、有的線程都是從這個QThread抽象類中派生的,要實現(xiàn)QThread中的純虛函數(shù)run(),run()函數(shù)是通過start()函數(shù)來實現(xiàn)調(diào)用的。1 class MyThread:public QThread2 public 3virtual void run();4;5 6void MyThread:run()78 for int count=0;count 20;count+)9 sleep(1);10 qDebug(Ping!);111213 14 int main()1516 MyThread a;17 MyThread b;18 19 a.start();/自動調(diào)用run(),否則即使
3、該線程創(chuàng)建,也是一開始就掛起20 b.start();21/要等待線程a,b都退出22 a.wait();23 b.wait();2425 Qt線程同步1.QMutex QMutex(bool recursive=FALSE)virtualQMutex()void lock()/試圖鎖定互斥量。如果另一個線程已經(jīng)鎖定這個互斥量,那么這次調(diào)用將阻塞直到那個線程把它解鎖。void unlock()bool locked()bool tryLock()/如果另一個進程已經(jīng)鎖定了這個互斥量,這個函數(shù)返回假,而不是一直等到這個鎖可用為止,比如,它不是阻塞的。1/Qt 2QMutex mutex;3 vo
4、id someMethod()45 mutex.lock 6qDebug(Hello);7 qDebug(World);8 mutex.unlock();910 11/用Java的術(shù)語,這段代碼應(yīng)該是:12 void someMethod()1314 synchronized15 qDebug(Hello);16 qDebug(World);1718不過在Qt中我們可用通過另一個類來簡化這種應(yīng)用,因為如果使用QMutex.lock()而沒有對應(yīng)的使用QMutex.unlcok()的話就會造成死鎖,別的線程永遠也得不到接觸該mutex鎖住的共享資源的機會。盡管可以不使用lock()而使用tryL
5、ock(timeout)來避免因為死等而造成的死鎖(tryLock(負值)=lock(),但是還是很有可能造成錯誤。對于上述的情況MFC中用CSingleLock或MultiLock,Boost中用boost:mutex:scoped_lock來進行解決,而在Qt中用QMutexLocker來進行解決。下面是沒有采用QMutexLocker的例子和采用QMutexLocker的方案。2.QMutexLocker this complex function locks aQMutex upon entering the function and unlocks the mutex at all
6、the exit points 1int complexFunction(int flag)23 mutex.lock 45 int retVal=0;6 7switch(flag)8 case 09 case 110 mutex.unlock();11 return moreComplexFunction(flag);12 case 21314 int status=anotherFunction();15 if(status 0)16 mutex.unlock();17 return-2;1819 retVal=status+flag;2021 break;22 default 23 if
7、(flag 10)24 mutex.unlock();25 return-1;2627 break;2829 30 mutex.unlock();31 return retVal;32This example increases the likelihood that errors will occur.Using QMutexLocker greatly simplifies the code,and makes it more readable:1 int complexFunction(int flag)23 QMutexLocker locker(&mutex);4 5int retV
8、al=0;6 7switch(flag)8 case 09 case 110 return moreComplexFunction(flag);11 case 21213 int status=anotherFunction();14 if(status 015 return-2;16 retVal=status+flag;1718 break;19 default 20 if(flag 10 21 return-1;22 break;2324 25 return retVal;26Now,the mutex will always be unlocked when the QMutexLoc
9、ker object is destroyed(when the function returns since locker is an auto variable).即使在拋出異常的情況下也可以使用。3.QReadWriteLock用mutex進行線程同步有一個問題就是mutex只允許某個時刻只允許一個線程對共享資源進行訪問,如果同時有多個線程對共享資源進行讀訪問,而只有一個寫操作線程,那么在這種情況下如果采用mutex就成為程序運行性能的瓶頸了。在這種情況下Qt下采用QReadWriteLock來實現(xiàn)多個線程讀,一個線程寫。寫線程執(zhí)行的時候會阻塞所有的讀線程,而讀線程之間的運行不需要進行同
10、步。1 MyData data;2 QReadWriteLock lock;3 void ReaderThread:run()45 6lock.lockForRead();7 access_data_without_modifying_it(&data);8 lock.unlock();9 1011 void WriterThread:run()1213 14 lock.lockForWrite();15 modify_data(&data);16 lock.unlock();17 1819 20 QReadWriterLock與QMutex相似,除了它對read,write訪問進行區(qū)別對待。
11、它使得多個讀者可以共時訪問數(shù)據(jù)。使用QReadWriteLock而不是QMutex,可以使得多線程程序更具有并發(fā)性。4.QReadLocker和QWriteLocker對于QMutex有QMutexLocker來簡化使用,而對于QReadWriteLock有QReadLocker和QWriteLocker。Heres an example that uses QReadLocker to lock and unlock aread-write lock for reading:QReadWriteLock lock;QByteArray readData()QReadLocker locker
12、(&lock);return data;It is equivalent to the following code:QReadWriteLock lock;QByteArray readData()lock.lockForRead();lock.unlock();return data;5.QSemaphore QSemaphore是QMutex的一般化,它可以保護一定數(shù)量的相同資源,與此相對,一個mutex只保護一個資源。下面例子中,使用QSemaphore來控制對環(huán)狀緩沖區(qū)的訪問,此緩沖區(qū)被生產(chǎn)者線程和消費者線程共享。生產(chǎn)者不斷向緩沖寫入數(shù)據(jù)直到緩沖末端,消費者從緩沖不斷從緩沖頭部讀取數(shù)
13、據(jù)。信號量比互斥量有更好的并發(fā)性,假如我們用互斥量來控制對緩沖的訪問,那么生產(chǎn)者,消費者不能同時訪問緩沖。然而,我們知道在同一時刻,不同線程訪問緩沖的不同部分并沒有什么危害。QSemaphore semaphore(1);|QMutex mutex;Qsemaphore.acquire();|Qmutex.lock();Qsemaphore.release();|Qmutex.unlock();Public Functions QSemaphore(int n=0)void acquire(int n=1)int available()constvoid release(int n=1)boo
14、l tryAcquire(int n=1)bool tryAcquire(int n,int timeout)Semaphores support two fundamental operations,acquire()andrelease acquire(n)tries to acquire nresources.If there arent that many resources available,the call will block until this is the case.release(n)releases nresources.returns immediately if
15、it cannot acquire the resourcesreturns the number of available resources at any time.Example:QSemaphore sem(5);/sem.available()=5 sem.acquire(3);/sem.available()=2 sem.acquire(2);/sem.available()=0 sem.release(5);/sem.available()=5 sem.release(5);/sem.available()=10 sem.tryAcquire(1);/sem.available(
16、)=9,returns true sem.tryAcquire(250);/sem.available()=9,returns false生產(chǎn)者線程寫數(shù)據(jù)到buffer直到緩沖末端,然后重新從buffer的頭部開始寫。顯然producer線程和consumer線程是需要進行同步的,Ifthe producer generates the data too fast,it will overwrite data that the consumer hasnt yet read;if the consumer reads the data too fast,it will pass the pro
17、ducer and read garbage.A crude way to solve this problem is to have the producer fill the buffer,then wait until the consumer has read the entire buffer,and so on.顯然這樣做效率是比較低的。1 const int DataSize=100000;2 const int BufferSize=8192;3 char bufferBufferSize;4 5/When the application starts,the reader t
18、hread will start/acquiringfreebytes and convert them intousedbytes 6QSemaphore freeBytes(BufferSize);/producer線程在此區(qū)域?qū)懭霐?shù)據(jù),初始資源數(shù)量為BufferSize 7QSemaphore usedBytes;/consumer線程讀取此區(qū)域的數(shù)據(jù),初始資源數(shù)量為0 89 10/For this example,each byte counts as one resource.11/In areal-world application,we would probably operat
19、e on larger/units(for example,64 or 256 bytes at atime)12 class Producer:public QThread 1314 public 15 void run();16;17/生產(chǎn)者每acquire一次就,使用掉Buffer個資源中的一個,而寫入的字符存入到buffer數(shù)組中/從而消費者可用讀取字符,從而消費者獲取一個資源18 void Producer:run()1920/qsrand(QTime(0,0,0).secsTo(QTime:currentTime();21 for int i=0;i DataSize;+i)22
20、freeBytes.acquire();23 bufferi%BufferSize=ACGTint)qrand()%4;24 usedBytes.release();252627 28 class Consumer:public QThread 2930 public 31 void run();32;33 34 void Consumer:run()3536 for int i=0;i DataSize;+i)37 usedBytes.acquire();38 fprintf(stderr,%c,bufferi%BufferSize);39 freeBytes.release();4041
21、fprintf(stderr,n);4243/Finally,in main(),we start the producer and consumer threads./What happens then is that the producer converts somefreespace/intousedspace,and the consumer can then convert it back to/freespace.46 int main(int argc,char*argv)4748 QCoreApplication app(argc,argv);49 Producer prod
22、ucer;50 Consumer consumer;51 producer.start();52 consumer.start();53 producer.wait();54 consumer.wait();55 return 0;56producer的run函數(shù):當producer線程執(zhí)行run函數(shù),如果buffer中已經(jīng)滿了,而沒有consumer線程沒有讀,這樣producer就不能再往buffer中寫字符。此時在freeBytes.acquire處就阻塞直到consumer線程讀(consume)數(shù)據(jù)。一旦producer獲取到一個字節(jié)(資源)就寫如一個隨機的字符,并調(diào)用usedByt
23、es.release從而consumer線程獲取一個資源可以讀一個字節(jié)的數(shù)據(jù)了。consumer的run函數(shù):當consumer線程執(zhí)行run函數(shù),如果buffer中沒有數(shù)據(jù),就是資源=0,則consumer線程在此處阻塞。直到producer線程執(zhí)行寫操作,寫入一個字節(jié),并執(zhí)行usedBytes.release從而使得consumer線程的可用資源數(shù)=1。則consumer線程從阻塞狀態(tài)中退出,并將usedBytes資源數(shù)-1,當前資源數(shù)=0。6.QWaitCondition bool wait(QMutex*mutex,unsigned long time=ULONG_MAX)void w
24、akeOne()void wakeAll()Public function:bool QWaitCondition:wait(QMutex*mutex,unsigned long time=ULONG_MAX)1)釋放鎖定的mutex 2)在線程對象上等待mutex必須由調(diào)用線程進行初鎖定。注意調(diào)用wait的話,會自動調(diào)用unlock解鎖之前鎖住的資源,不然會造成死鎖。線程1等待線程2來改變共享資源,從而達到一定的條件然后發(fā)出信號,使得線程1從wait中的阻塞狀態(tài)中被喚醒。但是線程2想改變資源,卻無法辦到,因為線程1調(diào)用lock之后就在wait中blocking,了但是沒有及時的unlock,
25、那么這就構(gòu)成了死鎖的條件。所以說wait函數(shù)除了使調(diào)用線程切換到內(nèi)核態(tài)之外,還自動unlock(&mutex)mutex將被解鎖,并且調(diào)用線程將會阻塞,直到下列條件之一滿足時才醒來:另一個線程使用wakeOne()或wakeAll()傳輸信號給它。在這種情況下,這個函數(shù)將返回真。time毫秒過去了。如果time為ULONG_MAX(默認值),那么這個等待將永遠不會超時(這個事件必須被傳輸)。如果等待的事件超時,這個函數(shù)將會返回假互斥量將以同樣的鎖定狀態(tài)返回。這個函數(shù)提供的是允許從鎖定狀態(tài)到等待狀態(tài)的原子轉(zhuǎn)換。void QWaitCondition:wakeAll()這將會喚醒所有等待QWait
26、Condition的線程。這些線程被喚醒的順序依賴于操組系統(tǒng)的調(diào)度策略,并且不能被控制或預(yù)知。void QWaitCondition:wakeOne()這將會喚醒所有等待QWaitCondition的線程中的一個線程。這個被喚醒的線程依賴于操組系統(tǒng)的調(diào)度策略,并且不能被控制或預(yù)知。假定每次用戶按下一個鍵,我們有三個任務(wù)要同時執(zhí)行,每個任務(wù)都可以放到一個線程中,每個線程的run()都應(yīng)該是這樣:QWaitCondition key_pressed;for(;)key_pressed.wait();/這是一個QWaitCondition全局變量/鍵被按下,做一些有趣的事do_something()
27、;或是這樣:forevermutex.lock();keyPressed.wait(&mutex);do_something();mutex.unlock();第四個線程回去讀鍵按下并且每當它接收到一個的時候喚醒其它三個線程,就像這樣:QWaitCondition key_pressed;for(;)getchar();/在key_pressed中導(dǎo)致引起任何一個線程。wait()將會從這個方法中返回并繼續(xù)執(zhí)行key_pressed.wakeAll();注意這三個線程被喚醒的順序是未定義的,并且當鍵被按下時,這些線程中的一個或多個還在do_something(),它們將不會被喚醒(因為它們現(xiàn)在
28、沒有等待條件變量)并且這個任務(wù)也就不會針對這次按鍵執(zhí)行操作。這種情況是可以避免得,比如,就像下面這樣做:1 QMutex mymutex;2 QWaitCondition key_pressed;3 int mycount=0;4 5/Worker線程代碼6 for(;)7 key_pressed.wait;/這是一個QWaitCondition全局變量/keyPressed.wait(&mutex);8 mymutex.lock 9mycount+;10 mymutex.unlock();11 do_something();12 mymutex.lock 13 mycount-;14 mym
29、utex.unlock();1516 17/讀取按鍵線程代碼18 for(;)19 getchar();20 mymutex.lock 21/睡眠,直到?jīng)]有忙碌的工作線程才醒來。count=0說明沒有Worker線程在do something 22 while(count 0)23 mymutex.unlock();24 sleep(1);25 mymutex.lock 2627 mymutex.unlock();28 key_pressed.wake All();2930應(yīng)用條件變量對前面用信號量進行保護的環(huán)狀緩沖區(qū)的例子進行改進:下面的例子中:1)生產(chǎn)者首先必須檢查緩沖是否已滿(numUs
30、edBytes=BufferSize),如果是,線程停下來等待bufferNotFull條件。如果不是,在緩沖中生產(chǎn)數(shù)據(jù),增加numUsedBytes,激活條件bufferNotEmpty。2)使用mutex來保護對numUsedBytes的訪問。另外,QWaitCondition:wait()接收一個mutex作為參數(shù),這個mutex應(yīng)該被調(diào)用線程初始化為鎖定狀態(tài)。在線程進入休眠狀態(tài)之前,mutex會被解鎖。而當線程被喚醒時,mutex會再次處于鎖定狀態(tài)。而且,從鎖定狀態(tài)到等待狀態(tài)的轉(zhuǎn)換是原子操作,這阻止了競爭條件的產(chǎn)生。當程序開始運行時,只有生產(chǎn)者可以工作。消費者被阻塞等待bufferNo
31、tEmpty條件,一旦生產(chǎn)者在緩沖中放入一個字節(jié),bufferNotEmpty條件被激發(fā),消費者線程于是被喚醒。1 const int DataSize=100000;2 const int BufferSize=8192;3 char bufferBufferSize;4 5QWaitCondition bufferNotEmpty;6 QWaitCondition bufferNotFull;7 QMutex mutex;8 int numUsedBytes=0;9 10 class Producer:public QThread 1112 public 13 void run();14;
32、15 16 void Producer:run()1718 qsrand(QTime(0,0,0).secsTo(QTime:currentTime();19 20 for int i=0;i DataSize;+i)21 mutex.lock/producer線程首先檢查緩沖區(qū)是否已滿22 if(numUsedBytes=BufferSize)/緩沖區(qū)已滿,等待consumer來減少numUsedBytes/bufferNotFull.wait(&mutex)先調(diào)用mutex.unlock()然后收到信號時調(diào)用mutex.lock 23 bufferNotFull.wait(&mutex);
33、/緩沖區(qū)已滿等待bufferNotFull的條件變量成立變?yōu)橛行盘?4 mutex.unlock();25 26 bufferi%BufferSize=ACGTint)qrand()%4;27 28 mutex.lock 29+numUsedBytes;/producer用掉一個Bytes,表示producer寫入buffer中的字節(jié)數(shù)30 bufferNotEmpty.wakeAll();31 mutex.unlock();323334 35 class Consumer:public QThread 3637 public 38 void run();39;40 41 void Consu
34、mer:run()4243 for int i=0;i DataSize;+i)44 mutex.lock 45 if(numUsedBytes=0 46 bufferNotEmpty.wait(&mutex);47 mutex.unlock();48 49 fprintf(stderr,%c,bufferi%BufferSize);50 51 mutex.lock 52-numUsedBytes;53 bufferNotFull.wakeAll();54 mutex.unlock();5556 fprintf(stderr,n);5758 59 int main(int argc,char*
35、argv)6061 QCoreApplication app(argc,argv);62 Producer producer;63 Consumer consumer;64 producer.start();65 consumer.start();66 producer.wait();67 consumer.wait();68 return 0;69另外一個例子:1#include qapplication.h 2#include qpushbutton.h 34/全局條件變量5 QWaitCondition mycond;6 7/Worker類實現(xiàn)8 class Worker:public
36、QPushButton,public QThread 910 Q_OBJECT 11 12 public 13 Worker(QWidget*parent=0,const char*name=0 14:QPushButton(parent,name)1516 setText(Start Working);17 18/連接從QPushButton繼承來的信號和我們的slotClicked()方法19 connect(this,SIGNAL(clicked(),SLOT(slotClicked();20 21/調(diào)用從QThread繼承來的start()方法這將立即開始線程的執(zhí)行22 QThread:start();2324 25 public slots:26 void slotClicked()2728/喚醒等待這個條件變量的一個線程29 mycond.wakeOne();3031 32 protected 33 void run()3435/這個方法將被新創(chuàng)建的線程調(diào)用36 37 while(TRUE)38/鎖定應(yīng)用程序互斥鎖,并且設(shè)置窗口標題來表明我們正在等待開始工作39 qApp lock 40 setC
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 學(xué)校物業(yè)考核管理辦法
- 備案流程及其管理辦法
- 宜昌校園防疫管理辦法
- 私募管理披露管理辦法
- 成立支部網(wǎng)格管理辦法
- 執(zhí)業(yè)藥師臨床管理辦法
- 安徽集體資產(chǎn)管理辦法
- 城市門面租賃管理辦法
- 工地油料考核管理辦法
- 戶籍民警輪崗管理辦法
- 《三國的世界》解說詞 第五集
- 漢字的發(fā)展(英文版介紹)Chinese-character
- 供貨方案及供貨計劃范文六篇
- 華為“1+X”職業(yè)技能等級(網(wǎng)絡(luò)系統(tǒng)建設(shè)與運維)中級考試題庫(含答案)
- 單位財務(wù)內(nèi)控制度
- 【電氣專業(yè)】15D501建筑物防雷設(shè)施安裝
- 山東省病原微生物實驗室及實驗活動備案管理系統(tǒng)
- DB41T 1564-2018豫南再生稻栽培技術(shù)規(guī)程
- 統(tǒng)編初中《道德與法治》課標解讀與新教材介紹課件
- GB/T 5975-1986鋼絲繩用壓板
- GB/T 17513-1998雄黃礦雌黃礦
評論
0/150
提交評論