值得思考的問題:
多個線程除了在時序上可能產生相互依賴,在其他方面是否也可能產生相互依賴呢?
生產消費者問題:
-有n個生產者同時制造產品,並把產品存入倉庫中
-有m個消費者同時需要從倉庫中取出產品
-規則:
當倉庫未滿,任意生產者可以存入產品
當倉庫未空,任意消費者可以取出產品
編程實驗:生產消費者問題
#include <QCoreApplication> #include <QThread> #include <QDebug>
static QString q_store; //通過全局變量來模擬唯一的倉庫
class Producer : public QThread { protected: void run() { int count = 0; while(true) { //每次產生的數字在0-10之間,模擬了生產者將生產好的產品(一個數字)放入到倉庫中(一個字符串類的對象)
q_store.append(QString::number((count++) % 10)); qDebug() << objectName() << " :" + q_store; //用字符串中字符表示當前倉庫中的商品
msleep(1); } } }; class Customer : public QThread { protected: void run() { while(true) { if(q_store != " ") { q_store.remove(0,1); //刪除字符串中的第一個字符,表示取出一個商品
qDebug() << objectName() << " : " + q_store; } msleep(1); } } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug() << "main begin()" ; Producer p; Customer c; p.setObjectName("Producer"); c.setObjectName("Customer"); p.start(); c.start(); qDebug() << "main end()"; return a.exec(); }
在程序中,如果只分析生產者是沒有問題的,同樣如果只分析消費者也是沒有問題的。出現問題的原因就是生產者和消費者是並行執行的,並且共享一個倉庫。兩個線程之間沒有任何的限制或約束。考慮一種情況,生產者在向倉庫中放入產品的同時,消費者從倉庫中取產品。相當於對共享資源同時讀寫,這肯定是不對的,也是不行的。
臨界資源(Critical Resource)
-每次只允許一個線程進行訪問(讀/寫)的資源
-線程間的互斥(競爭)
-多個線程在同一時刻都需要訪問臨界資源
QMutex類是一把線程鎖,保證線程間的互斥
-利用線程鎖能夠保證臨界資源的安全性
QMutex中的關鍵成員函數
-void lock()
當鎖空閑時,獲取鎖並繼續執行
當鎖被獲取,阻塞並等待鎖釋放
-void unlock()
釋放鎖(同一把鎖的獲取和釋放必須在同一線程中成對出現)
QMutex使用示例
QMutex mutex;
mutex.lock();
//do something with critical resource
mutex.unlock();
注意:如果mutex在調用unlock()時處於空閑狀態(即在調用lock函數之前就調用了unlock),那么程序的行為是未定的。所謂未定義就是什么時候產生bug,是不知道的。
解決生產者消費者問題:
#include <QCoreApplication> #include <QThread> #include <QDebug> #include <QMutex>
static QString q_store; //通過全局變量來模擬唯一的倉庫
static QMutex mutex; class Producer : public QThread { protected: void run() { int count = 0; while(true) { mutex.lock(); //每次產生的數字在0-10之間,模擬了生產者將生產好的產品(一個數字)放入到倉庫中(一個字符串類的對象)
q_store.append(QString::number((count++) % 10)); qDebug() << objectName() << " :" + q_store; //用字符串中字符表示當前倉庫中的商品
msleep(1); mutex.unlock(); } } }; class Customer : public QThread { protected: void run() { while(true) { mutex.lock(); if(q_store != " ") { q_store.remove(0,1); //刪除字符串中的第一個字符,表示取出一個商品
qDebug() << objectName() << " : " + q_store; } msleep(1); mutex.unlock(); } } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug() << "main begin()" ; Producer p; Customer c; p.setObjectName("Producer"); c.setObjectName("Customer"); p.start(); c.start(); qDebug() << "main end()"; return a.exec(); }