• 生产者-消费者问题


    可以用以下的思考步骤来完成这类问题:

    1. 有几类进程?每一类进程会对应一个函数
    2. 如何描述进程内的动作(执行内容)?考虑是否循环等
    3. 在每个执行步骤是否需要P?注意:有P必考虑在何处V
    4. 定义信号量
    5. 检查多个P连续操作?确定是否死锁

    在该类问题中死锁的情况基本只有请求和保持
    解决方法:1. PV连续出现,不可能死锁;2.多个P死锁,则尝试调整P操作的顺序

    例一
    在这里插入图片描述

    1. 三类进程:生产A、生产B、装配A+B
    p1() {} // 生产A 
    p2() {}	// 生产B
    cs() {} // 装配A+B
    
    • 1
    • 2
    • 3
    1. 确定各个进程的操作:一定要细致,每一步都要写上
      1. p1:生产A,送到F1
      2. p2:生产B,送到F2
      3. cs:从F1取一个A,从F2取一个B,组装A+B
      4. 是否需要循环?要
    p1() {
    	while(1){
    		生产A
    		送到F
    	}
    } // 生产A 
    p2() {
    	while(1){
    		生产B
    		送到F2
    	}
    }	// 生产B
    cs() {
    	while(1){
    		从F1取一个A
    		从F2取一个B
    		组装A+B
    	}
    } // 装配A+B
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    1. 确定互斥情况⭐
      1. 对P1:生产A之前,不需要;装到F1需要,F1相当于缓冲区,容量为10
      2. 对P2:与P1同理
      3. 对cs:取A/B之前判断是否F1/F2有,组装不需要互斥
    p1() {
    	while(1){
    		生产A
    		P(F1)
    		送到F1
    	}
    } // 生产A 
    p2() {
    	while(1){
    		生产B
    		P(F1)
    		送到F2
    	}
    } // 生产B
    cs() {
    	while(1){
    		P(full1)
    		从F1取一个A
    		P(full2)
    		从F2取一个B
    		组装A+B
    	}
    } // 装配A+B
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    这里就有很大的问题,还是那个道理,有P必V,建议就是做出一个P,先找在何处V,再去进行下一个PV的操作,这样就不容易出现缺漏

    p1() {
    	while(1){
    		生产A
    		P(F1)
    		送到F1
    		V(full1)	// 在cs中P(full1),当放入一个时,则需要V(full1)
    	}
    } // 生产A 
    p2() {
    	while(1){
    		生产B
    		P(F1)
    		送到F2
    		V(full1)	// 在cs中P(full2),当放入一个时,则需要V(full)
    	}
    }	// 生产B
    cs() {
    	while(1){
    		P(full1)
    		从F1取一个A
    		V(F1) 		// 在P1中P(F1),当取出一个时,则需要V(F1)
    		P(full2)
    		从F2取一个B
    		V(F1) 		// 在P2中P(F2),当取出一个时,则需要V(F2)
    		组装A+B
    	}
    } // 装配A+B
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    1. 定义信号量(就是简单的命名和美化、设置初始值)
    semaphore F1 = 10 // 表示F1货架的剩余容量
    semaphore F2 = 10 // 表示F2货架的剩余容量
    semaphore full1 = 0 // 表示F1货架上A产品
    semaphore full1 = 0 // 表示F2货架上B产品
    
    • 1
    • 2
    • 3
    • 4
    1. 最后这一步也是最容易被遗忘的一步,检查死锁问题和互斥资源访问
      这里不会产生死锁,但是货架的访问应为互斥,因此增加对货架的PV操作
    p1() {
    	while(1){
    		生产A
    		P(F1)
    		P(metux1)
    		送到F1
    		V(metux1)
    		V(full1)	
    	}
    } // 生产A 
    p2() {
    	while(1){
    		生产B
    		P(F1)
    		P(metux2)
    		送到F2
    		V(metux2)
    		V(full1)	
    	}
    }	// 生产B
    cs() {
    	while(1){
    		P(full1)
    		P(metux1)
    		从F1取一个A
    		V(metux1)
    		V(F1) 		
    		P(full2)
    		P(metux2)
    		从F2取一个B
    		V(metux2)
    		V(F1) 		
    		组装A+B
    	}
    } // 装配A+B
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    最后的答案(多加了一个判断货架是否有产品)
    在这里插入图片描述
    在这里插入图片描述

    小试牛刀
    在这里插入图片描述
    解答:

    1. 两类进程:小和尚和老和尚;
    2. 执行内容:小和尚抬水入缸,老和尚饮水,循环
    3. 需要互斥的资源:桶、水井、缸
    producer() { // 注释中的序号并不表示执行顺序,只表示一组PV操作的对应
    	while(1) {
    		p(tong) // 1
    		p(jing)	// 2(同一时间只有一个桶能取水)
    		桶取水
    		v(jing) // 2
    		p(empty) // 3(缸的容量有限)
    		p(gang)	// 4(同一时间只有一个桶能倒水入缸)
    		桶入缸
    		v(gang) // 4
    		v(full)	// 6(入缸后增加了缸里的水)
    		v(tong) // 1
    	}
    }
    
    consumer() {
    	while(10 {
    		p(tong) // 5
    		p(full)	// 6(表示缸里还有多少水)
    		p(gang) //7
    		入缸取一桶水
    		v(gang) //7
    		v(empty) // 3
    		喝水
    		v(tong) // 5
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    上述解答会产生死锁:若桶都被consumer拿走,但是缸中无水,此时producer也无法打水,程序死锁

    producer() { // 注释中的序号并不表示执行顺序,只表示一组PV操作的对应
    	while(1) {
    		p(empty) // 3(缸的容量有限,有空位才提桶)
    		p(tong) // 1
    		p(jing)	// 2(同一时间只有一个桶能取水)
    		桶取水
    		v(jing) // 2
    		p(gang)	// 4(同一时间只有一个桶能倒水入缸)
    		桶入缸
    		v(gang) // 4
    		v(full)	// 6(入缸后增加了缸里的水)
    		v(tong) // 1
    	}
    }
    
    consumer() {
    	while(10 {
    		p(full)	// 6(表示缸里还有多少水,有水才提桶)
    		p(tong) // 5
    		p(gang) //7
    		入缸取一桶水
    		v(gang) //7
    		v(empty) // 3
    		喝水
    		v(tong) // 5
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    【js逆向爬虫】-有道翻译js逆向实战
    【C#】FileInfo类 对文件进行操作
    diandian数据聚合平台参数分析(水)
    transformers的近期工作成果综述
    arm day1
    经典机器学习备忘录
    Pyhton专项进阶——http协议、cookie、session和认证-1
    使用react-grid-layout和echarts-for-react实现一个支持拖拽的自定义响应式dashboard页面
    Java开发篇——设计模式(5)装饰设计模式
    复习 --- select并发服务器
  • 原文地址:https://blog.csdn.net/weixin_47547625/article/details/127423289