QWaitCondition


在執行緒的同步化時,有些條件下執行緒必須等待,有些條件下則不用,這可以使用QWaitCondition來達到。

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

以下舉一個最簡單的:生產者每次生產一個int整數交給在店員上,而消費者從店員處取走整數,店員一次只能持有一個整數,以程式實例來看,首先是生產者:
  • Producer.h
#ifndef PRODUCER_H
#define PRODUCER_H

#include <QThread>

class Clerk;

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() {
// 生產1到10的整數
for(int product = 1; product <= 10; product++) {
// 暫停隨機時間
QThread::msleep(qrand() / 100);
// 將產品交給店員
clerk->setProduct(product);
}
}

再來是消費者:
  • Consumer.h
#ifndef CONSUMER_H
#define CONSUMER_H

#include <QThread>

class Clerk;

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() {
// 消耗10個整數
for(int i = 1; i <= 10; i++) {
// 暫停隨機時間
QThread::msleep(qrand() / 10);
// 從店員處取走整數
clerk->getProduct();
}
}

生產者將產品放至店員,而消費者從店員處取走產品,所以店員來決定誰必須等待並等候喚醒:
  • Clerk.h
#ifndef CLERK_H
#define CLERK_H

#include <QMutex>
#include <QWaitCondition>

class Clerk {
public:
Clerk();
void setProduct(int product);
int getProduct();

private:
int product;
QMutex mutex;
QWaitCondition waitCondition;
};

#endif

  • Clerk.cpp
#include "Clerk.h"
#include <iostream>
using namespace std;

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

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

if(this->product != -1) {
// 目前店員沒有空間收產品,請稍候!
waitCondition.wait(&mutex);
}

this->product = product;
cout << "生產者設定 " << this->product << endl;

// 喚醒一個消費者可以繼續工作了
waitCondition.wakeOne();

mutex.unlock();
}

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

if(this->product == -1) {
// 缺貨了,請稍候!
waitCondition.wait(&mutex);
}

int p = this->product;
cout << "消費者取走 " << this->product << endl;

this->product = -1;

// 喚醒一個生產者可以繼續工作了
waitCondition.wakeOne();

mutex.unlock();

return p;
}

使用這麼一個程式來測試:
  • main.cpp
#include <QCoreApplication>
#include "Clerk.h"
#include "Producer.h"
#include "Consumer.h"

int main(int argc, char *argv[]) {
QCoreApplication app(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;
}
執行結果:
生產者設定 (1)
消費者取走 (1)
生產者設定 (2)
消費者取走 (2)
生產者設定 (3)
消費者取走 (3)
生產者設定 (4)
消費者取走 (4)
生產者設定 (5)
消費者取走 (5)
生產者設定 (6)
消費者取走 (6)
生產者設定 (7)
消費者取走 (7)
生產者設定 (8)
消費者取走 (8)
生產者設定 (9)
消費者取走 (9)
生產者設定 (10)
消費者取走 (10)


生產者會生產10個整數,而消費者會消耗10個整數,由於店員處只能放置一個整數,所以每生產一個就消耗一個,其結果如上所示是無誤的。