生产者消费者问题(Windows下C语言版)

学习学累了,打打代码放松一下。

生产者消费者问题Producer-consumer problem

也称有限缓冲问题,描述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。

如何有效的解决生产者消费者问题?

解决该问题并不难,你可以选择二者同一时间只能出现一个,且第一个出现的必须是生产者。为此我们可以引入两类线程,分别代表生产者producer和消费者consumer,生产者线程用于生产数据,消费者线程用于消费数据,为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库,生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为;而消费者只需要从共享数据区中去获取数据,就不再需要关心生产者的行为。但是,这个共享数据区域中应该具备这样的线程间并发协作的功能:

  1. 共享数据区buffer处于满状态(T),阻塞生产者继续生产数据.
  2. 共享数据区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);
    }
}
  1. 消费者把最后一个 itemCount 的内容读出来,注意它现在是零。消费者返回到while的起始处,现在进入 if 块;
  2. 就在调用sleep之前,CPU决定将时间让给生产者,于是消费者在执行 sleep 之前就被中断了,生产者开始执行;
  3. 生产者生产出一项数据后将其放入缓冲区,然后在 itemCount 上加 1;
  4. 由于缓冲区在上一步加 1 之前为空,生产者尝试唤醒消费者;
  5. 遗憾的是,消费者并没有在休眠,唤醒指令不起作用。当消费者恢复执行的时候,执行 sleep,一觉不醒。出现这种情况的原因在于,消费者只能被生产者在 itemCount 为 1 的情况下唤醒;
  6. 生产者不停地循环执行,直到缓冲区满,随后进入休眠。

信号量

信号量(英语:semaphore)又称为信号标,是一个同步对象,用于保持在0至指定最大值之间的一个计数值。当线程完成一次对该semaphore对象的等待(wait)时,该计数值减一;当线程完成一次对semaphore对象的释放(release)时,计数值加一。当计数值为0,则线程等待该semaphore对象不再能成功直至该semaphore对象变成signaled状态。semaphore对象的计数值大于0,为signaled状态;计数值等于0,为nonsignaled状态.

过程:

  1. 初始化,给与它一个非负数的整数值。
  2. 运行P(wait()),信号标S的值将被减少。企图进入临界区段的进程,需要先运行P(wait())。当信号标S减为负值时,进程会被挡住,不能继续;当信号标S不为负值时,进程可以获准进入临界区段。
  3. 运行V(signal()),信号标S的值会被增加。结束离开临界区段的进程,将会运行V(signal())。当信号标S不为负值时,先前被挡住的其他进程,将可获准进入临界区段。

P操作:使 S=S-1 ,若 S>=0 ,则该进程继续执行,否则排入等待队列。

V操作:使 S=S+1 ,若 S>0 ,唤醒等待队列中的一个进程。

临界资源:同一时刻只允许一个进程访问的资源

#include
#include

//利用数组模拟缓冲区

#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下测试结果:

                                    生产者消费者问题(Windows下C语言版)_第1张图片

你可能感兴趣的:(操作系统)