1.什么是信号量(Semaphore)
信号量可以理解是对于 互斥量(Mutex) 功能的拓展,互斥量只能锁定一次而信号量可以获取多次,信号量可以用来保护一定数量的同种资源。信号量最典型的应用就是 生产者消费者模型。
生产者/消费者模型中对于同步的需求有两处:
- 如果生产者过快的生产数据,将会覆盖消费者还没来得及读取的数据;
- 如果消费者过快的消费数据,将会越过生产者并且消费到一些已经过期的数据;
针对以上的两个问题,有两种解决方案:
- 首先让生产者生产数据填满这个缓冲区,然后等待消费者消费整个缓冲区的数据,这是一种比较笨的方法,不推荐;
- 让生产者 和 消费者线程同时分别操作缓冲区的不同部分,这是一种比较高效的方法,推荐使用;
2.实现一个简单的生产者/消费者模型
创建 Qt
控制台项目。
(1) 头文件、缓冲区、信号量。
#include <QCoreApplication>
#include <QSemaphore>
#include <QThread>
#include <stdio.h>
const int DataSize = 10000;
const int bufferSize = 100;
int buffer[bufferSize];
//生产者可生产的缓冲区部分,初始为可以生产 bufferSize
QSemaphore freeBytes(bufferSize);
//消费者可读取的缓冲区部分,初始为可以读取 0
QSemaphore usedBytes(0);
(2) 生产者线程,继承自 QThread
类。
//生产者线程
class Producer : public QThread{
public:
Producer();
void run() override;
};
Producer::Producer(){}
void Producer::run(){
for(int i = 0;i < DataSize;i++){
//生产者先获取一个空闲单元,准备生产数据,如果此时缓冲区被生产者的生产的数据填满
//消费者还没读取,那么生产者线程就阻塞
freeBytes.acquire();
buffer[i % bufferSize] = (i % bufferSize);
//将消费者可以读取的资源 +1
usedBytes.release();
}
}
(3) 消费者线程,继承自 QThread
类。
//消费者线程
class Consumer : public QThread{
public:
Consumer();
void run() override;
};
Consumer::Consumer(){}
void Consumer::run(){
for(int i = 0;i < DataSize;i++){
//生产者先获取一个空闲单元,准备读取数据,如果此时缓冲区还没有数据,消费者线程就先阻塞
usedBytes.acquire();
fprintf(stderr,"%d",buffer[i % bufferSize]);
if(i % 16 == 0 && i != 0) fprintf(stderr,"\n");
//使这个单元变得空闲,以便生产者下次生产数据填充
freeBytes.release();
}
}
(4) 启动生产者 和 消费者,执行完任务之后退出。
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Producer producer;
Consumer consumer;
//启动 生产者和消费者线程
producer.start();
consumer.start();
//消费者 和 生产者线程执行完任务之后,自动退出
producer.wait();
consumer.wait();
return a.exec();
}