上一篇文章主要介紹了使用Windows API創建和使用信號量Semaphore
線程的互斥和同步(5)- Windows的信號量Semaphore
本篇文章只要介紹Qt的信號量 QSemaphore 類的使用,並用一個 生產者-消費者 實例來說明信號量是如何提高多線程的效率的。
下面是使用類 QSemaphore 常用的函數:
- void acquire (int n = 1); 表示請求n個資源,如果沒有任何可用資源,則阻塞。
- void release (int n = 1); 表示釋放n個資源。
- bool tryAcquire (int n = 1); 表示嘗試請求n個資源,如果沒有可用資源則會直接返回。也可以加上等待時間。
- int available () const;獲取當前可用資源的個數。
下面是一個 生產者-消費者 的使用示例說明:
在這個示例中,生產者向數組中寫入數據;消費者從數組中讀出數據,因爲他們同時操作的不是同一塊內存,因此同一時刻可以完全的併發執行。下面是一個示例圖:
圖畫的不太好看,因爲寫入和讀取的不是用一塊內存,因此可以同時進行。
當數組緩存滿了的時候,寫入線程阻塞;當可用資源爲空的時候,讀取線程等待資源寫入完成。
數據定義的代碼如下:
#include <QSemaphore>
#include <atomic>
extern "C" int g_data[20]; // 數據緩存
extern "C" QSemaphore g_semaphore; // 信號量
extern "C" std::atomic<int> g_nStartIndex; // 讀取數據的緩存起始位置
extern "C" std::atomic<int> g_nEndIndex; // 寫入數據的緩存起始位置
extern "C" std::atomic<int> g_nCurrentCreated; // 當前緩存可用數目
這裏定義了20個int類型的緩存數據。
全局變量的初始化,在cpp文件中
int g_data[20] = {-1};
QSemaphore g_semaphore(20);
std::atomic<int> g_nStartIndex(0);
std::atomic<int> g_nEndIndex(0);
std::atomic<int> g_nCurrentCreated(0);
生產者產生數據
生產者定義:
// 生產者
class ProducerThread : public CThread
{
public:
void run(void) override;
private:
int number = 0;
};
run函數爲線程的入口函數,CThread 是自定義線程類
生產者的實現:
void ProducerThread::run(void)
{
while (1)
{
// 請求一個資源
g_semaphore.acquire(1);
// 生產數據
g_data[g_nEndIndex] = number;
std::cout << "Created Data: " << number++ \
<< ", Index is " << g_nEndIndex << std::endl;
g_nEndIndex++;
if (g_nEndIndex == 20)
g_nEndIndex = 0;
::Sleep(100);
g_nCurrentCreated++;
}
}
生產者每個100ms生產一個數據。當緩存滿了的時候,
g_semaphore.acquire(1); 語句會處於阻塞的狀態,直到有資源被釋放。
消費者讀取數據
消費者定義:
// 消費者
class ConsumerThread : public CThread
{
public:
void run(void) override;
};
消費者實現:
void ConsumerThread::run(void)
{
while (1)
{
// 自旋等待
while (g_nCurrentCreated <= 0);
// 消費數據
std::cout << "Read Value: " << g_data[g_nStartIndex] \
<< ", Index is " << g_nStartIndex << std::endl;
g_nStartIndex++;
if (g_nStartIndex == 20)
g_nStartIndex = 0;
::Sleep(2000);
// 釋放資源
g_nCurrentCreated--;
g_semaphore.release();
}
}
這裏消費者,使用函數 g_semaphore.release(); 釋放資源。
消費者每隔2000ms釋放一次資源。
調用部分:
// 創建生產者和消費者
ConsumerThread consumer;
ProducerThread producer;
consumer.start();
producer.start();
// 等待線程結束
consumer.wait();
producer.wait();
運行結果:
Created Data: 0, Index is 0
Read Value: 0, Index is 0
Created Data: 1, Index is 1
Created Data: 2, Index is 2
Created Data: 3, Index is 3
Created Data: 4, Index is 4
Created Data: 5, Index is 5
Created Data: 6, Index is 6
Created Data: 7, Index is 7
Created Data: 8, Index is 8
Created Data: 9, Index is 9
Created Data: 10, Index is 10
Created Data: 11, Index is 11
Created Data: 12, Index is 12
Created Data: 13, Index is 13
Created Data: 14, Index is 14
Created Data: 15, Index is 15
Created Data: 16, Index is 16
Created Data: 17, Index is 17
Created Data: 18, Index is 18
Created Data: 19, Index is 19
Read Value: 1, Index is 1
Created Data: 20, Index is 0
Read Value: 2, Index is 2
Created Data: 21, Index is 1
Read Value: 3, Index is 3
Created Data: 22, Index is 2
Read Value: 4, Index is 4
Created Data: 23, Index is 3
首先生產者創建了一個數據,消費數據。因爲消費者消費數據比較慢,因此創建者很快就將緩存創建滿了。
當消費者再次消費數據後,生產者馬上創建數據。
作者:douzhq
個人博客主頁:不會飛的紙飛機
文章同步頁(可下載完整代碼):線程的互斥和同步(6)- Qt的信號量QSemaphore