在 Qt 中,信号量(QSemaphore)是一种用于控制对共享资源访问的同步工具。它允许一定数量的线程同时访问共享资源,适合用于生产者-消费者模型。
代码实现
#include <QCoreApplication>
#include <QThread>
#include <QSemaphore>
#include <QDebug>
#include <QQueue>
#include <QMutex>
const int BUFFER_SIZE = 5;
QQueue<int> buffer; // 共享缓冲区
QSemaphore freeSpace(BUFFER_SIZE); // 空闲空间信号量
QSemaphore usedSpace(0); // 已使用空间信号量
QMutex mutex; // 保护缓冲区的互斥锁
// 生产者线程
class Producer : public QThread {
protected:
void run() override {
for (int i = 0; i < 10; ++i) {
freeSpace.acquire(); // 等待空闲空间
mutex.lock();
buffer.enqueue(i);
qDebug() << "Produced:" << i;
mutex.unlock();
usedSpace.release(); // 增加已使用空间
QThread::msleep(100); // 模拟生产耗时
}
}
};
// 消费者线程
class Consumer : public QThread {
protected:
void run() override {
for (int i = 0; i < 10; ++i) {
usedSpace.acquire(); // 等待已使用空间
mutex.lock();
int value = buffer.dequeue();
qDebug() << "Consumed:" << value;
mutex.unlock();
freeSpace.release(); // 增加空闲空间
QThread::msleep(200); // 模拟消费耗时
}
}
};
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
Producer producer;
Consumer consumer;
producer.start();
consumer.start();
producer.wait();
consumer.wait();
return app.exec();
}
代码说明
1、共享资源:
buffer 是一个共享的队列,用于存储生产者生成的数据。
2、信号量:
freeSpace:表示缓冲区中的空闲空间数量,初始值为 BUFFER_SIZE。
usedSpace:表示缓冲区中已使用的空间数量,初始值为 0。
3、互斥锁:
mutex 用于保护对 buffer 的访问,确保生产者和消费者不会同时操作缓冲区。
4、生产者线程:
调用 freeSpace.acquire() 等待空闲空间。
使用 mutex.lock() 保护缓冲区,将数据放入缓冲区。
调用 usedSpace.release() 增加已使用空间。
5、消费者线程:
调用 usedSpace.acquire() 等待已使用空间。
使用 mutex.lock() 保护缓冲区,从缓冲区取出数据。
调用 freeSpace.release() 增加空闲空间。
为什么必须使用两个信号量?
1、精准控制两种状态:
freeSpace 确保生产者在缓冲区未满时才能生产。
usedSpace 确保消费者在缓冲区非空时才能消费。
2、避免竞争条件:
生产者和消费者通过不同的信号量等待和通知,避免因共享单一信号量导致的条件误判。
为什么不能只用一个 QSemaphore?
1、同步问题:
生产者需要等待缓冲区有空闲空间才能生产数据。
消费者需要等待缓冲区有数据才能消费数据。
如果只用一个 QSemaphore,无法同时满足这两个条件。
2、线程安全问题:
如果生产者请求资源(减少信号量),消费者释放资源(增加信号量),可能会导致信号量的值不符合实际缓冲区状态。
例如,生产者可能在缓冲区已满时继续生产,消费者可能在缓冲区为空时继续消费,导致数据不一致。
3、无法区分状态:
一个 QSemaphore 只能表示一种状态(如空闲空间或已使用空间),无法同时表示两种状态。