久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Qt多線程

 若渴若愚 2012-04-24

Qt多線程

Qt線程類

Qt 包含下面一些線程相關的類:
QThread 提供了開始一個新線程的方法
QThreadStorage 提供逐線程數(shù)據(jù)存儲
QMutex 提供相互排斥的鎖,或互斥量
QMutexLocker 是一個便利類,,它可以自動對QMutex加鎖與解鎖
QReadWriterLock 提供了一個可以同時讀操作的鎖
QReadLockerQWriteLocker 是便利類,它自動對QReadWriteLock加鎖與解鎖
QSemaphore 提供了一個整型信號量,,是互斥量的泛化
QWaitCondition 提供了一種方法,,使得線程可以在被另外線程喚醒之前一直休眠。

Qt線程的創(chuàng)建

Qt線程中有一個公共的抽象類,,所有的線程都是從這個QThread抽象類中派生的,要實現(xiàn)QThread中的純虛函數(shù)run(),run()函數(shù)是通過start()函數(shù)來實現(xiàn)調(diào)用的,。

復制代碼
1 class MyThread : public QThread {
2 public:
3 virtual void run();
4 };
5
6 void MyThread::run()
7 {
8 for( int count = 0; count < 20; count++ ) {
9 sleep( 1 );
10 qDebug( "Ping!" );
11 }
12 }
13
14 int main()
15 {
16 MyThread a;
17 MyThread b;
18
19 a.start();//自動調(diào)用run(),否則即使該線程創(chuàng)建,,也是一開始就掛起
20 b.start();
21 //要等待線程a,b都退出
22 a.wait();
23 b.wait();
24 }
25
復制代碼

Qt線程同步

1.QMutex

QMutex ( bool recursive = FALSE )

virtual ~QMutex ()

void lock () //試圖鎖定互斥量。如果另一個線程已經(jīng)鎖定這個互斥量,,那么這次調(diào)用將阻塞直到那個線程把它解鎖,。

void unlock ()

bool locked ()

bool tryLock () //如果另一個進程已經(jīng)鎖定了這個互斥量,這個函數(shù)返回假,,而不是一直等到這個鎖可用為止,,比如,它不是阻塞的,。

復制代碼
1 //Qt
2 QMutex mutex;
3 void someMethod()
4 {
5 mutex.lock();
6 qDebug("Hello");
7 qDebug("World");
8 mutex.unlock();
9 }
10
11 //用Java的術(shù)語,,這段代碼應該是:
12 void someMethod()
13 {
14 synchronized {
15 qDebug("Hello");
16 qDebug("World");
17 }

18 }

復制代碼

不過在Qt中我們可用通過另一個類來簡化這種應用,因為如果使用QMutex.lock()而沒有對應的使用QMutex.unlcok()的話

就會造成死鎖,,別的線程永遠也得不到接觸該mutex鎖住的共享資源的機會,。盡管可以不使用lock()而使用tryLock(timeout)

來避免因為死等而造成的死鎖( tryLock(負值)==lock()),但是還是很有可能造成錯誤。

對于上述的情況MFC中用CSingleLock 或 MultiLock,,Boost中用boost::mutex::scoped_lock來進行解決,,而在Qt中用

QMutexLocker來進行解決。下面是沒有采用 QMutexLocker的例子和采用QMutexLocker的方案,。

2.QMutexLocker

this complex function locks a QMutex upon entering the function and unlocks the mutex at all the exit points

復制代碼
1 int complexFunction(int flag)
2 {
3 mutex.lock();
4
5 int retVal = 0;
6
7 switch (flag) {
8 case 0:
9 case 1:
10 mutex.unlock();
11 return moreComplexFunction(flag);
12 case 2:
13 {
14 int status = anotherFunction();
15 if (status < 0) {
16 mutex.unlock();
17 return -2;
18 }
19 retVal = status + flag;
20 }
21 break;
22 default:
23 if (flag > 10) {
24 mutex.unlock();
25 return -1;
26 }
27 break;
28 }
29
30 mutex.unlock();
31 return retVal;
32 }
復制代碼

This example increases the likelihood that errors will occur.Using QMutexLocker greatly simplifies the code, and makes it more readable:

復制代碼
1 int complexFunction(int flag)
2 {
3 QMutexLocker locker(&mutex);
4
5 int retVal = 0;
6
7 switch (flag) {
8 case 0:
9 case 1:
10 return moreComplexFunction(flag);
11 case 2:
12 {
13 int status = anotherFunction();
14 if (status < 0)
15 return -2;
16 retVal = status + flag;
17 }
18 break;
19 default:
20 if (flag > 10)
21 return -1;
22 break;
23 }
24
25 return retVal;
26 }
復制代碼

Now, the mutex will always be unlocked when the QMutexLocker object is destroyed (when the function returns since locker is an auto variable).即使在拋出異常的情況下也可以使用,。

3. QReadWriteLock

用mutex進行線程同步有一個問題就是mutex只允許某個時刻只允許一個線程對共享資源進行訪問,,如果同時有多個線程對共享

資源進行讀訪問,而只有一個寫操作線程,,那么在這種情況下如果采用mutex就成為程序運行性能的瓶頸了,。在這種情況下Qt下采用

QReadWriteLock來實現(xiàn)多個線程讀,一個線程寫,。寫線程執(zhí)行的時候會阻塞所有的讀線程,,而讀線程之間的運行不需要進行同步。

復制代碼
1 MyData data;
2 QReadWriteLock lock;
3 void ReaderThread::run()
4 {
5
6 lock.lockForRead();
7 access_data_without_modifying_it(&data);
8 lock.unlock();
9
10 }
11 void WriterThread::run()
12 {
13
14 lock.lockForWrite();
15 modify_data(&data);
16 lock.unlock();
17
18 }
19
20
復制代碼

QReadWriterLock 與QMutex相似,,除了它對 "read","write"訪問進行區(qū)別對待,。它使得多個讀者可以共時訪問數(shù)據(jù)。使用QReadWriteLock而不是QMutex,,可以使得多線程程序更具有并發(fā)性,。

4.QReadLocker和QWriteLocker

對于QMutex有QMutexLocker來簡化使用,而對于QReadWriteLock有 QReadLockerQWriteLocker,。

Here's an example that uses QReadLocker to lock and unlock a read-write lock for reading:

復制代碼
QReadWriteLock lock;

QByteArray readData()
{
QReadLocker locker(&lock);

return data;
}
復制代碼

It is equivalent to the following code:

復制代碼
QReadWriteLock lock;

QByteArray readData()
{
lock.lockForRead();

lock.unlock();
return data;
}
復制代碼

5.QSemaphore

QSemaphoreQMutex的一般化,,它可以保護一定數(shù)量的相同資源,與此相對,,一個mutex只保護一個資源,。下面例子中,使用QSemaphore來控制對環(huán)狀緩沖區(qū)的訪問,,此緩沖區(qū)被生產(chǎn)者線程和消費者線程共享,。生產(chǎn)者不斷向緩沖寫入數(shù)據(jù)直到緩沖末端,消費者從緩沖不斷從緩沖頭部讀取數(shù)據(jù),。

信號量比互斥量有更好的并發(fā)性,,假如我們用互斥量來控制對緩沖的訪問,那么生產(chǎn)者,,消費者不能同時訪問緩沖,。然而,我們知道在同一時刻,,不同線程訪問緩沖的不同部分并沒有什么危害,。

QSemaphore semaphore(1); | QMutex mutex;

Qsemaphore.acquire(); | Qmutex.lock();

Qsemaphore.release(); | Qmutex.unlock();


Public Functions

Semaphores support two fundamental operations, acquire() and release():

  • acquire(n) tries to acquire n resources. If there aren't that many resources available, the call will block until this is the case.
  • release(n) releases n resources.
  • tryAcquire() returns immediately if it cannot acquire the resources
  • available() returns 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() == 9, returns true
sem.tryAcquire(250); // sem.available() == 9, returns false
復制代碼

生產(chǎn)者線程寫數(shù)據(jù)到buffer直到緩沖末端,然后重新從buffer的頭部開始寫,。

顯然producer線程和consumer線程是需要進行同步的,,If the producer generates the data too fast, it will overwrite data that the consumer hasn't yet read; if the consumer reads the data too fast, it will pass the producer 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 buffer[BufferSize];
4

5 //When the application starts, the reader thread will start

//acquiring "free" bytes and convert them into "used" bytes

6 QSemaphore freeBytes(BufferSize); //producer線程在此區(qū)域?qū)懭霐?shù)據(jù),,初始資源數(shù)量為BufferSize
7 QSemaphore usedBytes; //consumer線程讀取此區(qū)域的數(shù)據(jù),初始資源數(shù)量為0
8
9
10 //For this example, each byte counts as one resource.

11 //In a real-world application, we would probably operate on larger

//units (for example, 64 or 256 bytes at a time)

12 class Producer : public QThread
13 {
14 public:
15 void run();
16 };

17 //生產(chǎn)者每acquire一次就,,使用掉Buffer個資源中的一個,而寫入的字符存入到buffer數(shù)組中

//從而消費者可用讀取字符,從而消費者獲取一個資源

18 void Producer::run()
19 {
20 //qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
21 for (int i = 0; i < DataSize; ++i) {
22 freeBytes.acquire();
23 buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];
24 usedBytes.release();
25 }
26 }
27
28 class Consumer : public QThread
29 {
30 public:
31 void run();
32 };
33
34 void Consumer::run()
35 {
36 for (int i = 0; i < DataSize; ++i) {
37 usedBytes.acquire();
38 fprintf(stderr, "%c", buffer[i % BufferSize]);
39 freeBytes.release();
40 }
41 fprintf(stderr, "\n");
42 }

43 //Finally, in main(), we start the producer and consumer threads.

//What happens then is that the producer converts some "free" space

//into "used" space, and the consumer can then convert it back to //"free" space.


46 int main(int argc, char *argv[])
47 {

48 QCoreApplication app(argc, argv);

49 Producer producer;

50 Consumer consumer;

51 producer.start();

52 consumer.start();

53 producer.wait();

54 consumer.wait();

55 return 0;

56 }

復制代碼

producer的run函數(shù):

當producer線程執(zhí)行run函數(shù),,如果buffer中已經(jīng)滿了,,而沒有consumer線程沒有讀,這樣producer就不能再往buffer

中寫字符,。此時在 freeBytes.acquire處就阻塞直到consumer線程讀(consume)數(shù)據(jù),。一旦producer獲取到一個字節(jié)(資源)

就寫如一個隨機的字符,并調(diào)用 usedBytes.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

  • virtual ~QWaitCondition ()
  • bool wait ( QMutex * mutex, unsigned long time = ULONG_MAX )
  • void wakeOne ()
  • 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,,那么這就

    構(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 ()

    這將會喚醒所有等待QWaitCondition的線程。這些線程被喚醒的順序依賴于操組系統(tǒng)的調(diào)度策略,,并且不能被控制或預知,。

    void QWaitCondition::wakeOne ()

    這將會喚醒所有等待QWaitCondition的線程中的一個線程。這個被喚醒的線程依賴于操組系統(tǒng)的調(diào)度策略,,并且不能被控制或預知,。

假定每次用戶按下一個鍵,我們有三個任務要同時執(zhí)行,,每個任務都可以放到一個線程中,,每個線程的run()都應該是這樣:

復制代碼
QWaitCondition key_pressed;

for (;;) {
key_pressed.wait();
// 這是一個QWaitCondition全局變量
// 鍵被按下,做一些有趣的事
do_something();
}
復制代碼

或是這樣:

forever {
mutex.
lock();
keyPressed.wait(
&mutex);
do_something();
mutex.unlock();
}

第四個線程回去讀鍵按下并且每當它接收到一個的時候喚醒其它三個線程,,就像這樣:

復制代碼
QWaitCondition key_pressed;

for (;;) {
getchar();
// 在key_pressed中導致引起任何一個線程,。wait()將會從這個方法中返回并繼續(xù)執(zhí)行
key_pressed.wakeAll();
}
復制代碼

注意這三個線程被喚醒的順序是未定義的,并且當鍵被按下時,,這些線程中的一個或多個還在do_something(),,它們將不會被喚醒(因為它們現(xiàn)在沒有等待條件變量)并且這個任務也就不會針對這次按鍵執(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();
9 mycount++;

10 mymutex.unlock();

11 do_something();

12 mymutex.lock();
13 mycount--;
14 mymutex.unlock();
15 }
16
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();
26 }
27 mymutex.unlock();
28 key_pressed.wakeAll();
29 }
30
復制代碼

應用條件變量對前面用信號量進行保護的環(huán)狀緩沖區(qū)的例子進行改進:

下面的例子中:

1)生產(chǎn)者首先必須檢查緩沖是否已滿(numUsedBytes==BufferSize),,如果是,線程停下來等待bufferNotFull條件,。如果不是,,在緩沖中生產(chǎn)數(shù)據(jù),增加numUsedBytes,激活條件 bufferNotEmpty,。

2)使用mutex來保護對numUsedBytes的訪問,。

另外,QWaitCondition::wait()接收一個mutex作為參數(shù),,這個mutex應該被調(diào)用線程初始化為鎖定狀態(tài),。在線程進入休眠狀態(tài)之前,mutex會被解鎖,。而當線程被喚醒時,,mutex會再次處于鎖定狀態(tài)。

而且,,從鎖定狀態(tài)到等待狀態(tài)的轉(zhuǎn)換是原子操作,,這阻止了競爭條件的產(chǎn)生,。當程序開始運行時,只有生產(chǎn)者可以工作,。消費者被阻塞等待bufferNotEmpty條件,,一旦生產(chǎn)者在緩沖中放入一個字節(jié),bufferNotEmpty條件被激發(fā),,消費者線程于是被喚醒,。

復制代碼
1 const int DataSize = 100000;
2 const int BufferSize = 8192;
3 char buffer[BufferSize];
4
5 QWaitCondition bufferNotEmpty;
6 QWaitCondition bufferNotFull;

7 QMutex mutex;

8 int numUsedBytes = 0;
9
10 class Producer : public QThread
11 {
12 public:
13 void run();
14 };
15
16 void Producer::run()
17 {
18 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);//緩沖區(qū)已滿等待bufferNotFull的條件變量成立變?yōu)橛行盘?/span>
24 mutex.unlock();
25
26 buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];
27
28 mutex.lock();
29 ++numUsedBytes; //producer用掉一個Bytes,,表示producer寫入buffer中的字節(jié)數(shù)
30 bufferNotEmpty.wakeAll();
31 mutex.unlock();
32 }
33 }
34
35 class Consumer : public QThread
36 {
37 public:
38 void run();
39 };
40
41 void Consumer::run()
42 {
43 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", buffer[i % BufferSize]);
50
51 mutex.lock();
52 --numUsedBytes;
53 bufferNotFull.wakeAll();
54 mutex.unlock();
55 }
56 fprintf(stderr, "\n");
57 }
58
59 int main(int argc, char *argv[])
60 {
61 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>
3
4 // 全局條件變量
5 QWaitCondition mycond;
6
7 // Worker類實現(xiàn)
8 class Worker : public QPushButton, public QThread
9 {
10 Q_OBJECT
11
12 public:
13 Worker(QWidget *parent = 0, const char *name = 0)
14 : QPushButton(parent, name)
15 {
16 setText("Start Working");
17
18 // 連接從QPushButton繼承來的信號和我們的slotClicked()方法
19 connect(this, SIGNAL(clicked()), SLOT(slotClicked()));
20
21 // 調(diào)用從QThread繼承來的start()方法……這將立即開始線程的執(zhí)行
22 QThread::start();
23 }
24
25 public slots:
26 void slotClicked()
27 {
28 // 喚醒等待這個條件變量的一個線程
29 mycond.wakeOne();
30 }
31
32 protected:
33 void run()
34 {
35 // 這個方法將被新創(chuàng)建的線程調(diào)用……
36
37 while ( TRUE ) {
38 // 鎖定應用程序互斥鎖,,并且設置窗口標題來表明我們正在等待開始工作
39 qApp->lock();
40 setCaption( "Waiting" );
41 qApp->unlock();
42
43 // 等待直到我們被告知可以繼續(xù)
44 mycond.wait();
45
46 // 如果我們到了這里,我們已經(jīng)被另一個線程喚醒……讓我們來設置標題來表明我們正在工作
47 qApp->lock();
48 setCaption( "Working!" );
49 qApp->unlock();
50
51 // 這可能會占用一些時間,,幾秒,、幾分鐘或者幾小時等等,因為這個一個和GUI線程分開的線程,,在處理事件時,,GUI線程不會停下來……
52 do_complicated_thing();
53 }
54 }
55 };
56
57 // 主線程——所有的GUI事件都由這個線程處理。
58 int main( int argc, char **argv )
59 {
60 QApplication app( argc, argv );
61
62 // 創(chuàng)建一個worker……當我們這樣做的時候,,這個worker將在一個線程中運行
63 Worker firstworker( 0, "worker" );
64
65 app.setMainWidget( &worker );
66 worker.show();
67
68 return app.exec();
69 }
70
復制代碼

7.線程安全類

非線程安全類

這個類不是線程安全的,,因為假如多個線程都試圖修改數(shù)據(jù)成員 n,結(jié)果未定義。這是因為c++中的++和--操作符不是原子操作,。實際上,,它們會被擴展為三個機器指令:
1,把變量值裝入寄存器
2,,增加或減少寄存器中的值
3,,把寄存器中的值寫回內(nèi)存

假如線程A與B同時裝載變量的舊值,在寄存器中增值,,回寫,。他們寫操作重疊了,導致變量值僅增加了一次,。很明顯,,訪問應該串行化:A執(zhí)行123步驟時不應被打斷。

復制代碼
class Counter
{
public:
Counter() {n
=0;}
void increment() {++n;}
void decrement() {--n;}
int value() const {return n;}
private:
int n;
};
復制代碼

線程安全類

復制代碼
class Counter
{
public:
Counter() { n
= 0; }

void increment() { QMutexLocker locker(&mutex); ++n; }
void decrement() { QMutexLocker locker(&mutex); --n; }
int value() const { QMutexLocker locker(&mutex); return n; }

private:
mutable QMutex mutex;
int n;
};
復制代碼

QMutexLocker類在構(gòu)造函數(shù)中自動對mutex進行加鎖,,在析構(gòu)函數(shù)中進行解鎖,。隨便一提的是,,mutex使用了mutable關鍵字來修飾,,因為我們在value()函數(shù)中對mutex進行加鎖與解鎖操作,而value()是一個const函數(shù),。

    本站是提供個人知識管理的網(wǎng)絡存儲空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導購買等信息,,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多