C语言实战——生产者消费者问题

C语言实战——生产者消费者问题

方法摘要

生产者消费者共享缓冲区,生产者向缓冲区中放数据,消费者从缓冲取中取数据,当缓冲区中被放满时,生产者进程就必须进入挂起状态,直到消费者从缓冲中取走数据时,生产者才能继续向缓冲区中存放数据,同样当缓冲取中没有数据时,消费者进程就必须进入挂起休眠状态,直到生产者向缓冲区中放入数据时,消费者才能被唤醒继续从缓冲区中取走数据。

简写代码

  • 对于生产者:
void producer(void)
{
	int item;
	while(1)
	{
		item = produce_item();
		if(count == N)					//如果缓冲区满就休眠
		sleep();
		insert_item(item);
		count = count + 1;				//缓冲区数据项计数加1
		if(count == 1)
		wakeup(consumer);
	}
}
  • 这里的函数名只是简写,并没有按照要求,只是把其功能简单表述。
  • 对于消费者:
void consumer(void)
{
	int item;
	while(TRUE)
	{
		if(count == 0)				//如果缓冲区空就休眠
			sleep();
		item = remove_item();
		count = count - 1;			//缓冲区数据项计数减1
		if(count == N - 1)
			wakeup(producer);
		consume_item(item);
	}
}
  • 然而在特殊的情况下,即进程切换时,生产者唤醒消费者的新号可能会丢失,这样会导致生产者和消费者迟早都会休眠,导致死锁。

信号量的引入

  • down和up(分别为一般化后的sleep和wakeup)。对一个信号量执行down操作,则是检查其值是否大于0。若该值大于0,则将其减1(即用掉一个保存的唤醒信号)并继续;若该值为0,则进程将睡眠,而且此时down操作并未结束。检查数值、修改变量值以及可能发生的睡眠操作均作为一个单一的、不可分割的原子操作完成。保证一旦一个信号量操作开始,则在该操作完成或阻塞之前,其他进程均不允许访问该信号量。这种原子性对于解决同步问题和避免竞争条件是绝对必要的。所谓原子操作,是指一组相关联的操作要么都不间断地执行,要么不执行。
  • up操作对信号量的值增1。如果一个或多个进程在该信号量上睡眠,无法完成一个先前的down操作,则由系统选择其中的一个(如随机挑选)并允许该进程完成它的down操作。于是,对一个有进程在其上睡眠的信号量执行一次up操作后,该信号量的值仍旧是0,但在其上睡眠的进程却少了一个。信号量的值增加1和唤醒一个进程同样也是不可分割的,不会有某个进程因执行up而阻塞,正如前面的模型中不会有进程因执行wakeup而阻塞一样。
  • P,V操作的含义:P(S)表示申请一个资源,S.value>0表示有资源可用,其值为资源的数目;S.value=0表示无资源可用;S.value<0则他的大小表示S等待队列中的进程个数。V(S)表示释放一个资源,信号量的初值应大于等于0。P操作相当于“等待一个信号”,V操作相当于“发送一个新号”,在实现互斥过程中,V操作相当于发送一个信号说临界资源可用了。实际上,在实现互斥时,P、V操作相当于申请资源和释放资源。
  • 该解决方案使用了三个信号量:一个称为full,用来记录充满缓冲槽数目,一个称为empty,记录空的缓冲槽总数;一个称为mutex,用来确保生产者和消费者不会同时访问缓冲区。full的初值为0,empty的初值为缓冲区中槽的个数,mutex的初值是1。供两个或多个进程使用的信号量,其初值为1,保证同时只有一个进程可以进入临界区,称为二元信号量。如果每个进程在进入临界区前都执行了down操作,并在退出时执行一个up操作,就能够实现互斥。
#define N 100
typedef int semaphore;
semaphore mutex = 1;
semaphore empty = N;
semaphore full = 0;
void producer(void)
{
	int item;
	while(TRUE)
	{
		item = produce_item();
		down(&empty);				//空槽数目减1,相当于P(empty)
		down(&mutex);				//进入临界区,相当于P(mutex)
		insert_item(item);			//将新数据放到缓冲区中
		up(&mutex);				//离开临界区,相当于V(mutex)
		up(&full);				//满槽数目加1,相当于V(full)
	}
}
void consumer(void)
{
	int item;
	while(TRUE)
	{
		down(&full);				//将满槽数目减1,相当于P(full)
		down(&mutex);				//进入临界区,相当于P(mutex)
		item = remove_item();	   		 //从缓冲区中取出数据
		up(&mutex);				//离开临界区,相当于V(mutex)		
		up(&empty);				//将空槽数目加1 ,相当于V(empty)
		consume_item(item);			//处理取出的数据项
	}
}
  • 信号量的另一种用途是用于实现同步,信号量full和empty用来保证某种事件的顺序发生或不发生。在本例中,它们保证当缓冲区满的时候生产者停止运行,以及当缓冲区空的时候消费者停止运行。

代码实现

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#define N 100
#define true 1
#define producerNum  5
#define consumerNum  5
#define sleepTime 5

typedef int semaphore;
typedef int item;
item buffer[N] = {0};
int in = 0;
int out = 0;
int proCount = 0;
semaphore mutex = 1, empty = N, full = 0, proCmutex = 1;

void * producer(void * a)
{
    while(1)
	{
        while(proCmutex <= 0);
        proCmutex--;
        proCount++;
        printf("生产一个产品ID%d,缓冲区位置为%d\n",proCount,in);
        proCmutex++;

        while(empty <= 0)
		{
            printf("缓冲区已满!\n");
        }
        empty--;

        while(mutex <= 0);
        mutex--;

        buffer[in] = proCount;
        in = (in + 1) % N;

        mutex++;
        full++;
        sleep(sleepTime);
    }
}

void * consumer(void *b)
{
    while(1)
	{
        while(full <= 0)
		{
            printf("缓冲区为空!\n");
        }
        full--;

        while(mutex <= 0);
        mutex--;

        int nextc = buffer[out];
        buffer[out] = 0;//消费完将缓冲区设置为0

        out = (out + 1) % N;

        mutex++;
        empty++;

        printf(" 消费一个产品ID%d,缓冲区位置为%d\n", nextc,out);
        sleep(sleepTime);
    }
}

int main()
{
    pthread_t threadPool[producerNum+consumerNum];
    int i;
    for(i = 0; i < producerNum; i++){
        pthread_t temp;
        if(pthread_create(&temp, NULL, producer, NULL) == -1){
            printf("ERROR, fail to create producer%d\n", i);
            exit(1);
        }
        threadPool[i] = temp;
    }//创建生产者进程放入线程池


    for(i = 0; i < consumerNum; i++){
        pthread_t temp;
        if(pthread_create(&temp, NULL, consumer, NULL) == -1){
            printf("ERROR, fail to create consumer%d\n", i);
            exit(1);
        }
        threadPool[i+producerNum] = temp;
    }//创建消费者进程放入线程池


    void * result;
    for(i = 0; i < producerNum+consumerNum; i++){
        if(pthread_join(threadPool[i], &result) == -1){
            printf("fail to recollect\n");
            exit(1);
        }
    }//运行线程池
    return 0;
}
  • 15
    点赞
  • 122
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
下面是一个简单的生产者-消费者问题的C语言代码实现: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #define BUFFER_SIZE 10 int buffer[BUFFER_SIZE]; // 缓冲区数组 int counter = 0; // 缓冲区计数器 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 互斥锁 pthread_cond_t full = PTHREAD_COND_INITIALIZER; // 缓冲区满条件变量 pthread_cond_t empty = PTHREAD_COND_INITIALIZER; // 缓冲区空条件变量 void *producer(void *arg) { int val; while (1) { val = rand() % 100; // 随机生成数值 pthread_mutex_lock(&mutex); while (counter == BUFFER_SIZE) // 缓冲区满,等待空闲空间 pthread_cond_wait(&empty, &mutex); buffer[counter++] = val; // 放入缓冲区 printf("生产者:%d,缓冲区大小:%d\n", val, counter); pthread_mutex_unlock(&mutex); pthread_cond_signal(&full); // 发送缓冲区满信号 sleep(1); } return NULL; } void *consumer(void *arg) { int val; while (1) { pthread_mutex_lock(&mutex); while (counter == 0) // 缓冲区空,等待生产者 pthread_cond_wait(&full, &mutex); val = buffer[--counter]; // 从缓冲区取出 printf("消费者:%d,缓冲区大小:%d\n", val, counter); pthread_mutex_unlock(&mutex); pthread_cond_signal(&empty); // 发送缓冲区空信号 sleep(1); } return NULL; } int main(int argc, char *argv[]) { pthread_t tid1, tid2; srand(time(NULL)); pthread_create(&tid1, NULL, producer, NULL); pthread_create(&tid2, NULL, consumer, NULL); pthread_join(tid1, NULL); pthread_join(tid2, NULL); return 0; } ``` 在上面的代码中,使用了互斥锁和条件变量来实现线程同步。生产者线程和消费者线程分别使用互斥锁来保护共享变量(缓冲区计数器和缓冲区数组),并使用条件变量来等待对方的信号。当生产者向缓冲区放入数据时,如果缓冲区已满,则会等待消费者取走数据后再放入;当消费者从缓冲区取出数据时,如果缓冲区已空,则会等待生产者放入数据后再取出。这样就保证了生产者和消费者之间的同步和互斥。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lanciberrr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值