執行緒等待(QWaitCondition)

在執行緒的同步化時,通常執行緒執行各自的工作,但有些條件下執行緒必須等待,等待條件允許後才繼續進行工作,這時使用QWaitCondition來達到目的。


例如在生產者(Producer)與消費者(Consumer)的例子中,生產者會將產品交給店員,而消費者從店員處取走產品,店員一次只能持有固定數量產品,如果達到此數量,店員會叫生產者等一下,等到有空位再喚醒生產者繼續生產,如果店中沒有產品了,店員會叫消費者等一下,等到店中有產品了再喚醒消費者來取走產品。

以下範例生產者每次生產一個整數交給店員,而消費者從店員處取走整數,最後我們可以看到流程,依序生產->拿走->生產->拿走……。


clerk.h

#ifndef CLERK
#define CLERK

#include<QDebug>
#include <QMutex>
#include <QWaitCondition>

class Clerk {
public:
    Clerk();
    void setProduct(int product);
    void getProduct();
private:
    int m_product;
    QMutex mutex;
    QWaitCondition waitCondition;
};

#endif 

clerk.cpp

#include "clerk.h"

Clerk::Clerk() {
    m_product = -1;
}

void Clerk::setProduct(int product) {
    mutex.lock();

    if(m_product != -1) {
        waitCondition.wait(&mutex); //如果店員手上有產品,生產者執行緒停在此處
    }

    //開始生產產品
    m_product = product;      //生產後將店員手中產品編號
    qDebug() << "生產者產生: " << m_product ;
    waitCondition.wakeOne();  //喚醒消費者執行緒,可以開始拿走產品

    mutex.unlock();
}

void Clerk::getProduct() {
    mutex.lock();

    if(m_product == -1) {
        waitCondition.wait(&mutex);  //如果店員手上沒有產品,消費者執行緒停在此處
    }

    //開始拿走產品
    qDebug() << "消費者取走: " << m_product ;
    m_product = -1;           //取走後將店員手中產品編號設為-1
    waitCondition.wakeOne();  //喚醒生產者執行緒,可以開始生產產品

    mutex.unlock();
}

consumer.h

#ifndef CONSUMER
#define CONSUMER

#include <QThread>
#include"clerk.h"

class Consumer : public QThread {
public:
    Consumer(Clerk *clerk);
protected:
    void run();
private:
    Clerk *clerk;
};

#endif 

consumer.cpp

#include "consumer.h"
#include "clerk.h"

Consumer::Consumer(Clerk *clerk) {
    this->clerk = clerk;
}

void Consumer::run() {
    for(int i = 1; i <= 10; i++) {
        QThread::msleep(500);
        clerk->getProduct();
    }
}

producer.h

#include <QThread>
#include"clerk.h"

class Producer : public QThread {
public:
    Producer(Clerk *clerk);
protected:
    void run();
private:
    Clerk *clerk;
};

#endif 

producer.cpp

#include "producer.h"
#include "clerk.h"

Producer::Producer(Clerk *clerk) {
    this->clerk = clerk;
}

void Producer::run() {
    for(int product = 1; product <= 10; product++) {
        QThread::msleep(1000);
        clerk->setProduct(product);
    }
}

main.cpp

#include <QCoreApplication>
#include "clerk.h"
#include "producer.h"
#include "consumer.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Clerk *clerk = new Clerk;
    Producer *producer = new Producer(clerk);
    Consumer *consumer = new Consumer(clerk);

    producer->start();
    consumer->start();
    producer->wait();
    consumer->wait();

    return 0;
}

QWaitCondition