線程的互斥和同步(6)- Qt的信號量QSemaphore

上一篇文章主要介紹了使用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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章