QReadWriteLock 與 QSemaphore


使用 QMutex 與 QMutexLocker 時,被鎖定的區域一次只允許一個執行緒,其它執行緒必須等待解除鎖定,方可嘗試取得鎖定並執行程式,在大量執行緒存取共用資源的情況下,執行緒的等待必然造成效能上的瓶頸。

在某些時候,共用資源是可以被多個執行緒以唯讀方式進行讀取,而不會影響資源共用的安全性,在這種情況下,不必讓執行緒等待,您可以使用QReadWriteLock來區分唯讀或寫入的共用鎖定,例如:
QReadWriteLock lock;

void ReaderThread::run() {
    ...
    lock.lockForRead();
    // 讀取共用資料
    lock.unlock();
    ...
}

void WriterThread::run() {
    ...
    lock.lockForWrite();
    // 設定、寫入共用資源
    lock.unlock();
    ...
}

QReadWriteLock的lockForRead()方法,在共用資源正在進行設定或寫入,也就是另一個區域已被lockForWrite()時,才進行執行緒的阻斷,如果沒有設定或寫入的動作,則lockForRead()並不會讓其它執行緒等待。

QReadLocker與QWriteLocker為一個方便的類別,以QReadWriteLock物件為引數來建構,建構時進行讀取鎖定或寫入鎖定,解構時解除鑜定。

有些共用資源擁有一定的可存取次數,在多執行緒存取的情況下,可以同時允許一定數量的執行緒來存取共用資源,您可以自行計數執行緒進入與離開的次數,例如在
QWaitCondition 中生產者與消費者的例子,若店員可以持有不只一個產品,則生產者或消費者可以存取店員共用區的次數,則必須自行實作計算。

您也可以直接使用QSemaphore,它為您提供計數信號,在建構QSemaphore,可以指定資源可獲取的量(次數),不設定則預設為0,您可以使用acqire()來表示將存取多少資源,使用release()表示將釋放多少資源,使用available()來得知有多少資源可以存取,例如:
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

在 Qt 的文件中有個 Semaphores Example,使用QSemaphore來實作生產者與消費者,您可以觀看其 原始程式碼,該程式有一個8192空間的char型態buffer陣列,可以被生產者與消費者存取,其中:
void Producer::run() {
     qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
     for (int i = 0; i < DataSize; ++i) {
         freeBytes.acquire();
         buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];
         usedBytes.release();
     }
}

freeBytes為QSemaphore的實例,初始為 8192次的資源,每acqire()一次,表示將用掉buffer陣列中的一個索引位置,用以限制生產者可以放入buffer陣列的字元數量,而 usedBytes為QSemaphore的實例,是用來告訴消費者,buffer中已使用的索引量,每次存入buffer一個字元,就release一 次usedBytes,表示消費者可以多消費一次字元,在消費者這邊:
void Consumer::run() {
     for (int i = 0; i < DataSize; ++i) {
         usedBytes.acquire();
         fprintf(stderr, "%c", buffer[i % BufferSize]);
         freeBytes.release();
     }
     fprintf(stderr, "\n");
}

每當消費者要取出buffer中的一個字元時,就acqiire一次usedBytes,表示用掉一個索引位置,最後再release一次freeBytes,這用以告知生產者,多出一個可以存放buffer的次數。