天天看點

QT5.14.2 官方例子 - Qt Core 1: Semaphores Example(多信号)一.系列總連結:二.項目位置:三.項目描述:四.官網講解: 五.思路及邏輯解析:六.知識鞏固

一.系列總連結:

QT5.14.2 官方例子 - 學習系列

https://blog.csdn.net/qq_22122811/article/details/108007519

二.項目位置:

Examples\Qt-5.14.2\corelib\threads\semaphores

注:在Examples下的路徑

項目子產品:corelib\threads

2.1: 資源下載下傳:

管道1:

下載下傳qtcreator源碼,會附帶該例程;

管道2:

github下載下傳連結:

https://github.com/peterwei24/QT5.14.2_Examples/tree/master/allExamplesCode/corelib/threads/semaphores

三.項目描述:

示範了生産者往緩存中寫入資料,通過信号量保持消費者讀取資料的同時性。

項目效果:

QT5.14.2 官方例子 - Qt Core 1: Semaphores Example(多信号)一.系列總連結:二.項目位置:三.項目描述:四.官網講解: 五.思路及邏輯解析:六.知識鞏固

這個是官方例子的顯示結果,不太友善明白過程,我修改了一下,顯示結果如下:

QT5.14.2 官方例子 - Qt Core 1: Semaphores Example(多信号)一.系列總連結:二.項目位置:三.項目描述:四.官網講解: 五.思路及邏輯解析:六.知識鞏固

大緻能看到過程是圍繞着生産者生産完,消費者開始消費的過程,這就符合通過信号量來保持線程間互斥性的設計;

四.官網講解:

https://doc.qt.io/qt-5.14/qtcore-threads-semaphores-example.html

示範了使用Qt的多線程程式設計。

生産者将資料寫入緩沖區,直到它到達緩沖區的末尾,然後從頭開始重新啟動,覆寫現有的資料。消費者線程在生成資料時讀取資料并将其寫入标準錯誤。

信号量使它可能擁有比互斥鎖更進階别的并發性。如果對緩沖區的通路是由QMutex保護的,那麼消費者線程不能與産生線程同時通路緩沖區。不過,讓兩個線程同時處理緩沖區的不同部分也沒有什麼壞處。

該示例包含兩個類:生産者和消費者。兩者都繼承自QThread。用于這兩個類之間通信的循環緩沖區和保護它的信号量是全局變量。

使用QSemaphore來解決生産者-消費者問題的另一種方法是使用QWaitCondition和QMutex。這就是等待條件示例所做的事情。

五.思路及邏輯解析:

(對例子進行了部分修改,友善閱讀和了解,和項目描述中修改結果一緻!)

全局變量:

const int DataSize = 10;

const int BufferSize = 3;
char buffer[BufferSize];

QSemaphore freeBytes(BufferSize);
QSemaphore usedBytes;
           

DataSize是生産者将生成的資料量。為了使這個例子盡可能簡單,我們把它設為常數。BufferSize是循環緩沖區的大小。它小于DataSize,這意味着在某個時間點,生産者将到達緩沖區的末尾,并從開始重新開始。

為了同步生産者和消費者,我們需要兩個信号量。freeBytes信号量控制緩沖區的“空閑”區域(生産者還沒有填充資料或者消費者已經讀取的區域)。usedBytes信号量控制緩沖區的“已使用”區域(生産者已經填充但消費者還沒有讀取的區域)。

這些信号量一起確定生産者永遠不會超過消費者之前的BufferSize位元組,并且消費者永遠不會讀取生産者還沒有生成的資料。

freeBytes信号量是用BufferSize初始化的,因為最初整個緩沖區是空的。usedBytes信号量初始化為0(如果沒有指定,則為預設值)。

生産者類:

class Producer : public QThread
{
public:
    void run() override
    {
        for (int i = 0; i < DataSize; ++i) {
            // 目前信号量是否有可以填充資料的緩沖區,如果資源計數為0,則沒有可以填充的緩沖區,
            //     那麼該函數會阻塞目前線程
            freeBytes.acquire();
            buffer[i % BufferSize] = "ACGT"[QRandomGenerator::global()->bounded(4)];
            // 使useSpace對象的資源計數加1,此時消費者線程就有機會取資料了
            usedBytes.release();
        }
    }
};
           

生産者生成資料的DataSize位元組。在将一個位元組寫入循環緩沖區之前,它必須使用freeBytes信号量獲得一個“空閑”位元組。如果消費者沒有跟上生産者的進度,QSemaphore::acquire()調用可能會阻塞。(即消費沒跟上,生産就會一定時間後停滞!)

最後,生産者使用usedBytes信号量釋放一個位元組。已成功地将“空閑”位元組轉換為“已使用”位元組,準備由使用者讀取。

補充:

參照:https://blog.csdn.net/hitzsf/article/details/109105288

1.QRandomGenerator: 允許從高品質随機數生成器擷取随機值。

QRandomGenerator :: global()傳回QRandomGenerator的全局執行個體,Qt将確定該執行個體被安全地播種。

  • quint32 bounded(quint32 highest)

    生成[0,hightest) 範圍内的quint32 類型的随機的整數

2.void QSemaphore::acquire(int n = 1):試圖擷取由信号量保護的n個資源。如果n > available(),這個調用将阻塞,直到有足夠的資源可用。

3.void QSemaphore::release(int n = 1):釋放由信号量保護的n個資源。這個函數也可以用來“建立”資源。例如:

消費者類:

class Consumer : public QThread
{
    Q_OBJECT
public:
    void run() override
    {
        for (int i = 0; i < DataSize; ++i) {
            // 目前信号量是否有可以取資料的緩沖區,如果資源計數為0,則沒有可取的緩沖區,那麼該函數            
            //     會阻塞目前線程
            usedBytes.acquire();
            fprintf(stderr, "%c", buffer[i % BufferSize]);
            // 使freeSpace對象的資源計數加1,此時生産者線程就有機會生産資料了
            freeBytes.release();
        }
        fprintf(stderr, "\n");
    }
};
           

代碼與生産者非常相似,除了這一次我們獲得一個“已使用”位元組并釋放一個“空閑”位元組,而不是相反。

main()函數:

在main()中,我們建立了兩個線程,并調用QThread::wait()來確定在我們退出之前兩個線程都有時間完成:

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    Producer producer;
    Consumer consumer;
    producer.start();
    consumer.start();
    producer.wait();
    consumer.wait();
    return 0;
}
           

六.知識鞏固

https://www.cnblogs.com/alinh/p/6905221.html

信号量的了解

“信号量用在多線程多任務同步的,一個線程完成了某一個動作就通過信号量告訴别的線程,别的線程再進行某些動作(大家都在semtake的時候,就阻塞在 哪裡)。而互斥鎖是用在多線程多任務互斥的,一個線程占用了某一個資源,那麼别的線程就無法通路,直到這個線程unlock,其他的線程才開始可以利用這 個資源。比如對全局變量的通路,有時要加鎖,操作完了,在解鎖。有的時候鎖和信号量會同時使用的”

也就是說,信号量不一定是鎖定某一個資源,而是流程上的概念,比如:有A,B兩個線程,B線程要等A線程完成某一任務以後再進行自己下面的步驟,這個任務 并不一定是鎖定某一資源,還可以是進行一些計算或者資料處理之類。而線程互斥量則是“鎖住某一資源”的概念,在鎖定期間内,其他線程無法對被保護的資料進 行操作。在有些情況下兩者可以互換。

互斥量和信号量的差別

1. 互斥量用于線程的互斥,信号量用于線程的同步。

這是互斥量和信号量的根本差別,也就是互斥和同步之間的差別。

互斥:是指某一資源同時隻允許一個通路者對其進行通路,具有唯一性和排它性。但互斥無法限制通路者對資源的通路順序,即通路是無序的。

同步:是指在互斥的基礎上(大多數情況),通過其它機制實作通路者對資源的有序通路。在大多數情況下,同步已經實作了互斥,特别是所有寫入資源的情況必定是互斥的。少數情況是指可以允許多個通路者同時通路資源。

2. 互斥量值隻能為0/1,信号量值可以為非負整數。(這個似乎不太好了解,信号量實作多個同類資源的多線程互斥和同步)

也就是說,一個互斥量隻能用于一個資源的互斥通路,它不能實作多個資源的多線程互斥問題。信号量可以實作多個同類資源的多線程互斥和同步。當信号量為單值信号量是,也可以完成一個資源的互斥通路。

3. 互斥量的加鎖和解鎖必須由同一線程分别對應使用,信号量可以由一個線程釋放,另一個線程得到。

繼續閱讀