欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

一起talk C栗子吧(第一百零六回:C语言实例--生产者与消费者问题二)

程序员文章站 2022-05-13 16:34:37
各位看官们,大家好,上一回中咱们说的是生产者与消费者问题的例子,这一回咱们继续说该例子。闲话休提,言归正转。让我们一起talk C栗子吧! 我们在上一回中介绍了生产者与消费者...

各位看官们,大家好,上一回中咱们说的是生产者与消费者问题的例子,这一回咱们继续说该例子。闲话休提,言归正转。让我们一起talk C栗子吧!

我们在上一回中介绍了生产者与消费者问题,并且给出了该问题的解决思路和伪代码。我们在今天的章回中将把伪代码转换为C语言代码,也就是说通过具体的代码实现生产者与消费者问题

详细的实现步骤如下:

1.自己定义信号量联合类型和共享内存结构体类型; 2.使用程序中main的参数:-p和-c分别表示生产者和消费者进程;

以上步骤是生产者和消费者共同使用的步骤,下面分别列出生产者和消费者各自的实现步骤。
生产者的实现步骤:

1.在生产者中创建信号量,并且初始化信号量; 2.在生产者中创建共享内存,并且把共享内存的地址链接到生产者进程空间中; 3.对信号量进行P操作; 4.判断池子中产品的数量是否小于池子的容量,如果小于,那么进入下一步,否则等待消费者取走产品; 5.生产者生产一个产品,并且把产品放到池子中;然后把产品的数量增加一; 6.对信号量进行V操作; 7.重复步骤3到6,直到池子中不能存放产品为止; 8.删除信号量; 9.把共享内存的地址链接从生产者地址空间中断开,并且删除共享内存;

消费者的实现步骤:

1.在消费中获取已经在生产者中创建的信号量; 2.在消费者中获取已经在生产者中创建的共享内存,并且把共享内存的地址链接到消费者进程空间中; 3.对信号量进行P操作; 4.判断池子中产品的数量是否大于零;如果大于零,那么进入下一步,否则等待生产者生产产品; 5.消费者从池子中取走一个产品;然后把产品的数量减少一; 6.对信号量进行V操作; 7.重复步骤3到6,直到池子中没有产品为止; 8.把共享内存的地址链接从消费者地址空间中断开;

看官们,正文中就不写代码了,详细的代码放到了我的资源中,大家可以下载使用。

在代码中,我们封装了信号量的P/V操作,使代码更加简单一些。

我们对生产者生产产品和消费者取走产品的动作进行了延时,主要是为了方便观看程序运行结果,进而更好地演示生产者与消费者问题 ;其中对消费者和生产者的延时可以自己控制,代码中对消费者的延时是生产者的延时的一半。

另外,池子的大小也是可以自己定义的,代码中池子的大小为8,可以容纳8个int类型的变量。

我们在代码中使用共享内存这种方式进行生产者进程和消费者进程之间的通信,大家可以使用其它进程间的通信方式。比如:管道,FIFO以及消息队列。

有看官提问说,能不能使用一个全局变量和全局数组代替共享内存。我的回答是不能,因为全局变量只是在一个进程中是全局的,而我们使用了两个进程,这显然不行。说的更加直接点,如果生产者进程更新了全局变量和全局数组,那么消费者进程根本看不到生产者更新的内容,在消费者看来它们没有变,或者说产品数量为零而且池子也是空的。

下面是程序的运行结果,请大家参考

$ ./a -p &             //在后台运行编译后的程序,使用参数-p启动生产者进程
[1] 3562               //生产者进程已经启动,PID为3562
$ [pid: 3562 ] producer is running 
producer count: 0       //生产者生产产品前,产品数量为空,池子里没有任何产品
produce data. count:1,data:1 
producer count: 1 
produce data. count:2,data:2 
producer count: 2 
produce data. count:3,data:3  //生产者生产了3个产品,并且存放到了池子中
./a -c                //在前台运行编译后的程序,使用参数-c启动消费者进程
[pid: 3563 ] customer is running  //生产者进程已经启动,PID为3563
customer count: 3      //在消费者取走产品前,产品数量为3,池子里有3个产品        
get data. count:3,data:3  //消费者取走1个产品
producer count: 2 
produce data. count:3,data:3 //生产者生产了1个产品
customer count: 3 
get data. count:3,data:3 
customer count: 2 
get data. count:2,data:2    //消费者又取走了2个产品
producer count: 1 
produce data. count:2,data:2  //生产者又生产了1个产品
customer count: 2 
get data. count:2,data:2 
customer count: 1 
get data. count:1,data:1    //消费者取走2个产品
producer count: 0 
produce data. count:1,data:1  //生产者又生产了1个产品
customer count: 1 
get data. count:1,data:1 
customer count: 0                   
the buf is empty             //消费者取走1个产品,池子中没有产品了 
[pid: 3563 ] customer running finished  //消费者进程结束
$ producer count: 0 
produce data. count:1,data:1 
producer count: 1 
produce data. count:2,data:2 
producer count: 2 
produce data. count:3,data:3 
producer count: 3 
produce data. count:4,data:4 
producer count: 4 
produce data. count:5,data:5 
producer count: 5 
produce data. count:6,data:6 
producer count: 6 
produce data. count:7,data:7 
producer count: 7 
produce data. count:8,data:8 
producer count: 8 
the buf is full             //生产者连续生产了8 个产品,池子已经満了
[pid: 3562 ] producer running finished   //生产者结束
                                                                                                            //输入回车键,返回到终端中
[1]+  Done        

从程序的运行结果中可以看到,生产者进程和消费进程交替运行,一个进程向池子中添加产品,另外一个进程从池子中取走产品。但是无论如何,池子中产品的数量在生产者和消费者看来都是一致的,这受益于信号量对生产者进程和消费者进程进行了同步操作。此外,我们对代码做了处理,当池子已经满了,生产者便停止生产产品,同时结束生产者进程,而不是一直等待消费者从池子中取走产品。当池子中没有产品时,消费者不再从池子中取走产品,同时结束消费者进程,而不是一直等待生产者生产产品。不然我们的程序就永远也不能结束了。

各位看官,关于生产者与消费者问题的例子咱们就说到这里。欲知后面还有什么例子,且听下回分解 。