学习学累了,打打代码放松一下。
生产者消费者问题Producer-consumer problem
也称有限缓冲问题,描述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
如何有效的解决生产者消费者问题?
解决该问题并不难,你可以选择二者同一时间只能出现一个,且第一个出现的必须是生产者。为此我们可以引入两类线程,分别代表生产者producer和消费者consumer,生产者线程用于生产数据,消费者线程用于消费数据,为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库,生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为;而消费者只需要从共享数据区中去获取数据,就不再需要关心生产者的行为。但是,这个共享数据区域中应该具备这样的线程间并发协作的功能:
- 共享数据区buffer处于满状态(T),阻塞生产者继续生产数据.
- 共享数据区buffer处于空状态(F),阻塞消费者继续使用数据.
一个错误的想法
int itemCount = 0;
procedure producer() {
while (true) {
item = produceItem();
if (itemCount == BUFFER_SIZE) {
sleep();
}
putItemIntoBuffer(item);
itemCount = itemCount + 1;
if (itemCount == 1) {
wakeup(consumer);
}
}
}
procedure consumer() {
while (true) {
if (itemCount == 0) {
sleep();
}
item = removeItemFromBuffer();
itemCount = itemCount - 1;
if (itemCount == BUFFER_SIZE - 1) {
wakeup(producer);
}
consumeItem(item);
}
}
- 消费者把最后一个 itemCount 的内容读出来,注意它现在是零。消费者返回到while的起始处,现在进入 if 块;
- 就在调用sleep之前,CPU决定将时间让给生产者,于是消费者在执行 sleep 之前就被中断了,生产者开始执行;
- 生产者生产出一项数据后将其放入缓冲区,然后在 itemCount 上加 1;
- 由于缓冲区在上一步加 1 之前为空,生产者尝试唤醒消费者;
- 遗憾的是,消费者并没有在休眠,唤醒指令不起作用。当消费者恢复执行的时候,执行 sleep,一觉不醒。出现这种情况的原因在于,消费者只能被生产者在 itemCount 为 1 的情况下唤醒;
- 生产者不停地循环执行,直到缓冲区满,随后进入休眠。
信号量
信号量(英语:semaphore)又称为信号标,是一个同步对象,用于保持在0至指定最大值之间的一个计数值。当线程完成一次对该semaphore对象的等待(wait)时,该计数值减一;当线程完成一次对semaphore对象的释放(release)时,计数值加一。当计数值为0,则线程等待该semaphore对象不再能成功直至该semaphore对象变成signaled状态。semaphore对象的计数值大于0,为signaled状态;计数值等于0,为nonsignaled状态.
过程:
- 初始化,给与它一个非负数的整数值。
- 运行P(
wait()
),信号标S的值将被减少。企图进入临界区段的进程,需要先运行P(wait()
)。当信号标S减为负值时,进程会被挡住,不能继续;当信号标S不为负值时,进程可以获准进入临界区段。 - 运行V(
signal()
),信号标S的值会被增加。结束离开临界区段的进程,将会运行V(signal()
)。当信号标S不为负值时,先前被挡住的其他进程,将可获准进入临界区段。
P操作:使 S=S-1
,若 S>=0
,则该进程继续执行,否则排入等待队列。
V操作:使 S=S+1
,若 S>0
,唤醒等待队列中的一个进程。
临界资源:同一时刻只允许一个进程访问的资源
#include<stdio.h>
#include<windows.h>
//利用数组模拟缓冲区
#define SIZE 3 //缓冲区容量大小
typedef int semaphore; //信号量
/*
mutex =1 : 表示没有进程进入临界区
mutex =0 : 表示有一个进程进入临界区,前方没有其他进程。
mutex =-1 :表示一个进程已经进入,另一个进程的等待进入。
*/
semaphore mutex = 1;//互斥锁
semaphore empty = SIZE;//缓冲区中未使用个数
semaphore full = 0;//缓冲区中已使用个数
semaphore buffer[SIZE];//创建缓冲区
semaphore front = 0, rear = 0;
void producer_Therad();//生产者进程
void consumer_Thread();//消费者进程
void P(semaphore& pass) {// P操作 passeren 荷兰语 通过
pass--;
}
void V(semaphore& release) {// V操作 vrijgevern 荷兰语 释放
release++;
}
void produce_item(char* item_ptr) {//生产者生产食物 并标记为T True 代表该处已满
*item_ptr = 'T' ;
}
void put_buffer(int pass) {//生产者将物品放入缓冲区
buffer[front] = pass;
printf("produce is %c into buffer[%d]\n",buffer[front],front);
front = (front +1) % SIZE;
}
void remove_buffer(char* release) {//消费者消费缓冲区的食物,并标记为F false 代表该处已空
printf("consumer use is %c from buffer[%d]\n",buffer[rear], rear);
*release = buffer[rear];
buffer[rear] = 'F';
printf("now the buffer[%d] is %c !\n",rear, buffer[rear]);
rear = (rear + 1) % SIZE;
}
void consume_item() {
printf("consumer had use the product\n\n");
}
void producer_Thread()
{
char item;
while (1) {
Sleep(1000);
produce_item(&item);
P(empty);
P(mutex);
put_buffer(item);
V(mutex);
V(full);
if (full == SIZE)
consumer_Thread();
}
}
void consumer_Thread()
{
char get_item;
while (1) {
Sleep(1000);
P(full);
P(mutex);
remove_buffer(&get_item);
V(mutex);
V(empty);
consume_item();
if (empty == SIZE)
producer_Thread();
}
}
int main()
{
producer_Thread();
return 0;
}
VS2019下测试结果: