生产者-消费者问题实现(Ubuntu环境)

一、实验要求

Linux操作系统下用C实现经典同步问题:生产者—消费者,具体要求如下:
(1)一个大小为10的缓冲区,初始状态为空。
(2)2个生产者,随机等待一段时间,往缓冲区中添加数据,若缓冲区已满,等待消费者取走数据之后再添加,重复10次。
(3)2个生产者,随机等待一段时间,往缓冲区中读取数据,若缓冲区为空,等待生产者添加数据之后再读取,重复10次。
(4)当前只能有一个进程对这个缓冲区进行操作 。
(5)缓冲区采用循环队列表示,利用头、尾指针来存放、读取数据,以及判断队列是否为空。缓冲区中数组大小为10。
(6)利用随机函数rand()得到A~Z的一个随机字符,作为生产者每次生产的数据,存放到缓冲区中。
(7)使用shmget()系统调用实现共享主存段的创建,shmget()返回共享内存区的ID。对于已经申请到的共享段,进程需把它附加到自己的虚拟空间中才能对其进行读写。
(8)信号量的建立采用semget()函数,同时建立信号量的 数量。在信号量建立后,调用semctl()对信号量进行初始2化 ,例如本实验中,可以建立两个信号量SEM_EMPTY、 SEM_FULL,初始化时设置SEM_EMPTY为10,SEM_FULL 为0。使用操 作信号的函数semop()做排除式操作,使用这个 函数防止对共享内存的同时操作。对共享内存操作完毕后采用shmctl()函数撤销共享内存段。
(9)使用循环,创建2个生产者以及2个消费者,采用函数 fork()创建一个新的进程。
(10)一个进程的一次操作完成后,采用函数fflush()刷新缓冲区。
(11)程序最后使用semctl()函数释放内存。

二、实现(Ubuntu环境)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/types.h>
 
#define MAX_BUFFER_SIZE 10
#define SHM_MODE 0600
#define SEM_MODE 0600
 
#define SEM_FULL 0
#define SEM_EMPTY 1
#define MUTEX 2
/*
#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
//	 union   semun   is   defined   by   including   <sys/sem.h>   
#else 
//   according   to   X/OPEN   we   have   to   define   it   ourselves  
union semun{
	int val;
	struct semid_ds *buf;
	unsigned short *array;
};
#endif
union semun su;//sem union,用于初始化信号量
*/
 
struct my_buffer
{
	int head;
	int tail;
	char str[MAX_BUFFER_SIZE];
	int num;  //缓冲区里字母数量
	int is_empty;
};
 
const int N_CONSUMER = 2;//消费者数量
const int N_PRODUCER = 2;//生产者数量
const int N_BUFFER = 10;//缓冲区容量
const int N_WORKTIME = 10;//工作次数
int shm_id = -1;
int sem_id = -1;
pid_t child;
pid_t parent;
 
//得到10以内的一个随机数  
int get_random()  
{  
    int digit;  
    srand((unsigned)(getpid() + time(NULL)));  
    digit = rand() % 10;  
    return digit;  
}  
 
//得到A~Z的一个随机字母  
char getRandChar()
{
    char letter;  
    srand((unsigned)(getpid() + time(NULL)));  
    letter = (char)((rand() % 26) + 'A');  
    return letter;  
}
 
//sem_id 表示信号量集合的 id
//sem_num 表示要处理的信号量在信号量集合中的索引
//P操作
void waitSem(int sem_id,int sem_num)
{
	struct sembuf sb;
	sb.sem_num = sem_num;
	sb.sem_op = -1;//表示要把信号量减一
	sb.sem_flg = SEM_UNDO;//
	//第二个参数是 sembuf [] 类型的,表示数组
	//第三个参数表示 第二个参数代表的数组的大小
	if(semop(sem_id,&sb,1) < 0){
		perror("waitSem failed");
		exit(1);
	}
}
 
//V操作
void sigSem(int sem_id,int sem_num)
{
	struct sembuf sb;
	sb.sem_num = sem_num;
	sb.sem_op = 1;
	sb.sem_flg = SEM_UNDO;
	//第二个参数是 sembuf [] 类型的,表示数组
	//第三个参数表示 第二个参数代表的数组的大小
	if(semop(sem_id,&sb,1) < 0){
		perror("sigSem failed");
		exit(1);
	}
}
 
//打印进程运行结果
void printTime()
{
	//打印时间
	time_t now;
	struct tm *timenow;         //实例化tm结构指针
	time(&now);
	timenow = localtime(&now);
	printf("执行时间: %s ",asctime(timenow));
}
 
int main(int argc, char ** argv)
{
	shm_id = shmget(IPC_PRIVATE,MAX_BUFFER_SIZE,SHM_MODE);   //申请共享内存
	if(shm_id < 0)
	{
		perror("create shared memory failed");
		exit(1);
	}
 
	struct my_buffer *shmptr;  
	shmptr = shmat(shm_id, 0, 0);   //将申请的共享内存附加到申请通信的进程空间
	if (shmptr == (void*)-1)
	{  
        perror("add buffer to using process space failed!\n");  
        exit(1);  
    }  
 
	if((sem_id = semget(IPC_PRIVATE,3,SEM_MODE)) < 0)
	{                  								//创建三个信号量,SEM_EMPTY,SEM_FULL和MUTEX
		perror("create semaphore failed! \n");
		exit(1);
	}
 
	if(semctl(sem_id,SEM_FULL,SETVAL,0) == -1)
	{												//将索引为0的信号量设置为0-->SEM_FULL
		perror("sem set value error! \n");		
		exit(1);
	}
 
	if(semctl(sem_id,SEM_EMPTY,SETVAL,10) == -1)
	{												//将索引为1的信号量设置为10-->SEM_EMPTY
	 	perror("sem set value error! \n");
	 	exit(1);
	}
	if(semctl(sem_id,MUTEX,SETVAL,1) == -1)
	{												//将索引为3的信号量设置为1-->MUTEX
	 	perror("sem set value error! \n");
	 	exit(1);
	}
 
	shmptr -> head = 0;  
    shmptr -> tail = 0;  
    shmptr -> is_empty = 1;  
    shmptr -> num = 0;
 
	for(int i = 0; i < N_PRODUCER; i++)
	{
		parent = fork();
		if(parent < 0)
		{
			perror("the fork failed");
			exit(1);
		}
		else if(parent == 0)
		{
			shmptr = shmat(shm_id, 0, 0);   //将申请的共享内存附加到申请通信的进程空间
			if (shmptr == (void*)-1)
			{  
        		perror("add buffer to using process space failed!\n");  
        		exit(1);  
    		}  
			int count = 0;
			for(int j = 0; j < N_WORKTIME; j++)
			{
				waitSem(sem_id, SEM_EMPTY);
				waitSem(sem_id, MUTEX);
				sleep(get_random()); 
 
				printf("-------------------------------------------------------------\n");
				printf("我是第 %d 个生产者进程,PID = %d\n", i + 1, getpid());
 
				/*生产产品*/
				char c = getRandChar();                                      //随机获取字母
				shmptr -> str[shmptr->tail] = c;
                shmptr -> tail = (shmptr->tail + 1) % MAX_BUFFER_SIZE;  
                shmptr -> is_empty = 0;           //写入新产品  
				shmptr -> num++;
 
				/*打印输出结果*/
				printTime();              //程序运行时间
 
				int p;
				printf("缓冲区数据(%d个):",shmptr -> num);                   //打印缓冲区中的数据
				p = (shmptr->tail-1 >= shmptr->head) ? (shmptr->tail-1) : (shmptr->tail-1 + MAX_BUFFER_SIZE);  
                for (p; !(shmptr -> is_empty) && p >= shmptr -> head; p--)  
                {  
                    printf("%c", shmptr -> str[p % MAX_BUFFER_SIZE]);  
                }  
                printf("\t 生产者 %d  放入 '%c'. \n", i + 1, c);  
				printf("-------------------------------------------------------------\n");
 
				fflush(stdout);
				sigSem(sem_id, MUTEX);
				sigSem(sem_id, SEM_FULL);
			}
			//将共享段与进程之间解除连接  
            shmdt(shmptr);  
            exit(0); 
		} 
	}
 
	for(int i = 0; i < N_CONSUMER; i++)
	{
		child = fork();
		if(child < 0)//调用fork失败
		{
			perror("the fork failed");
			exit(1);
		}
		else if(child == 0)
		{
			int count = 0; 
			shmptr = shmat(shm_id, 0, 0);   //将申请的共享内存附加到申请通信的进程空间
			if (shmptr == (void*)-1)
			{  
        		perror("add buffer to using process space failed!\n");  
        		exit(1);  
    		} 
			for(int j = 0; j < N_WORKTIME; j++)
			{
 
				waitSem(sem_id, SEM_FULL);
				waitSem(sem_id, MUTEX);
				sleep(get_random()); 
 
				printf("-------------------------------------------------------------\n");
				printf("我是第 %d 个消费者进程,PID = %d\n", i + 1, getpid());
				/*消费数据*/
				char lt = shmptr -> str[shmptr -> head];  
                shmptr -> head = (shmptr -> head + 1) % MAX_BUFFER_SIZE;  
                shmptr -> is_empty = (shmptr->head == shmptr->tail);  //
				shmptr -> num--;
				/*打印输出结果*/
				printTime(); //程序运行时间
 
				int p;
				printf("缓冲区数据(%d个):",shmptr -> num);                   //打印缓冲区中的数据
				p = (shmptr -> tail - 1 >= shmptr -> head) ? (shmptr -> tail-1) : (shmptr -> tail - 1 + MAX_BUFFER_SIZE);  
                for (p; !(shmptr -> is_empty) && p >= shmptr -> head; p--)  
                {  
                    printf("%c", shmptr -> str[p % MAX_BUFFER_SIZE]);  
                }  
                printf("\t 消费者 %d  取出 '%c'. \n", i + 1, lt);  
				printf("-------------------------------------------------------------\n");
 
				fflush(stdout);
				sigSem(sem_id,MUTEX);
				sigSem(sem_id,SEM_EMPTY);
			}
			//将共享段与进程之间解除连接  
        	shmdt(shmptr);  
        	exit(0);
		}  
	}
 
	    //主进程最后退出  
    while (wait(0) != -1);  
    //将共享段与进程之间解除连接  
    shmdt(shmptr);  
    //对共享内存区执行控制操作  
    shmctl(shm_id,IPC_RMID,0);//当cmd为IPC_RMID时,删除该共享段  
    shmctl(sem_id,IPC_RMID,0);  
    printf("主进程运行结束!\n");  
    fflush(stdout);  
    exit(0); 
	return 0;
}

三、执行结果

有2个生产者,随机等待一段时间,往缓冲区中添加数据,若缓冲区已满,等待消费者取走数据之后再添加,重复10次。有2个消费者,随机等待一段时间,从缓冲区中读取数据,若缓冲区为空,等待生产者添加数据之后再读取,重复10次。运行程序每行的打印顺序:生产者/消费者标号,PID、缓冲区数据、生产或消费的数据、进程执行的时间。如下图所示:
在这里插入图片描述

  • 11
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Ubuntu操作系统上,可以使用pthread库来实现生产者消费者问题。具体实现步骤如下: 1. 引入头文件 ```c++ #include <pthread.h> #include <queue> ``` 2. 定义全局变量和互斥锁、条件变量 ```c++ pthread_mutex_t mutex; pthread_cond_t cond; queue<int> q; int maxSize = 10; ``` 3. 实现生产者函数和消费者函数 ```c++ void *producer(void *arg) { int i = 0; while (i < 20) { pthread_mutex_lock(&mutex); if (q.size() == maxSize) { pthread_cond_wait(&cond, &mutex); } q.push(i); cout << "Producer produces " << i << endl; i++; pthread_mutex_unlock(&mutex); pthread_cond_signal(&cond); } return NULL; } void *consumer(void *arg) { int data; while (true) { pthread_mutex_lock(&mutex); if (q.empty()) { pthread_cond_wait(&cond, &mutex); } data = q.front(); q.pop(); cout << "Consumer consumes " << data << endl; pthread_mutex_unlock(&mutex); pthread_cond_signal(&cond); if (data == 19) { break; } } return NULL; } ``` 4. 在主函数中创建线程并启动 ```c++ int main() { pthread_t tid1, tid2; pthread_mutex_init(&mutex, NULL); pthread_cond_init(&cond, NULL); pthread_create(&tid1, NULL, producer, NULL); pthread_create(&tid2, NULL, consumer, NULL); pthread_join(tid1, NULL); pthread_join(tid2, NULL); pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); return 0; } ``` 需要注意的是,在使用互斥锁和条件变量时,需要使用pthread_mutex_lock、pthread_mutex_unlock、pthread_cond_wait和pthread_cond_signal等函数来实现线程之间的同步和协作。同时,需要在条件变量的等待函数中传入互斥锁指针来保证线程安全。当条件不满足时,线程进入等待状态,等待其他线程的通知。 以上是在Ubuntu操作系统上,使用pthread库实现生产者消费者问题的具体步骤。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值