三个进程同步问题---(生产者-消费者,哲学家进餐,读者-写者)

一:生产者-消费者问题(Producer-Consumer Problem)
 
问题描述
 
- 场景:多个生产者进程向缓冲区投放数据,消费者进程从中取出数据,缓冲区大小固定(如n个槽位)。
 
- 核心矛盾:
 
- 互斥:同一时刻只能有一个进程访问缓冲区(临界资源)。
 
- 同步:生产者需等待缓冲区未满,消费者需等待缓冲区非空。
 
关键解决方案(信号量机制)
 
1. 信号量定义:
 
-  mutex :互斥信号量,初值为1,保证对缓冲区的互斥访问。
 
-  empty :同步信号量,初值为 n (缓冲区空槽数),控制生产者能否放入数据。
 
-  full :同步信号量,初值为0(缓冲区数据项数),控制消费者能否取出数据。
 
2. 生产者代码逻辑:


while (1) {
    生产数据item;
    wait(empty);    // 等待空槽位
    wait(mutex);    // 互斥访问缓冲区
    将item放入缓冲区;
    signal(mutex);  // 释放互斥锁
    signal(full);   // 通知消费者有数据可用
}


  
3. 消费者代码逻辑:

 
while (1) {
    wait(full);     // 等待数据可用
    wait(mutex);    // 互斥访问缓冲区
    从缓冲区取出数据item;
    signal(mutex);  // 释放互斥锁
    signal(empty);  // 通知生产者有空槽位
}


考点延伸
 
- 变种:单缓冲区(无需 mutex ,仅需 empty 和 full )、多生产者-多消费者。
生产者-消费者单缓冲区问题
 
问题描述:
 
- 缓冲区大小为1(仅能存放1个数据),生产者每次生产一个数据放入缓冲区,消费者每次从缓冲区取出一个数据。
 
- 核心矛盾:
 
- 缓冲区为空时,消费者需等待;缓冲区为满时,生产者需等待。
 
- 单缓冲区无需考虑互斥(同一时刻只能有一个进程访问),仅需解决同步问题。


- 易错题点:信号量顺序(先同步后互斥,避免死锁)。
 
二、读者-写者问题(Readers-Writers Problem)
 
问题描述
 
- 场景:共享数据可被多个读者同时读取,但写者必须独占访问(读写互斥、写写互斥)。
 
- 核心矛盾:
 
- 第一类读者-写者问题:优先保证读者,可能导致写者饥饿。
 
- 第二类读者-写者问题:优先保证写者,减少写者等待时间。
 
关键解决方案(信号量机制)
 
第一类读者-写者(读者优先)
 
1. 信号量定义:
 
-  read_count :记录当前读者数,初值为0,需用 mutex 互斥保护。
 
-  mutex :保护 read_count 的互斥信号量,初值为1。
 
-  write_lock :读写互斥信号量,初值为1(写者获取锁,读者仅在无写者时读取)。
 
2. 读者代码逻辑:

 
wait(mutex);          // 互斥访问read_count
if (read_count == 0)  
    wait(write_lock); // 第一个读者需阻塞写者
read_count++;        
signal(mutex);        // 释放互斥锁
读取数据;  
wait(mutex);          // 互斥访问read_count
read_count--;        
if (read_count == 0)  
    signal(write_lock); // 最后一个读者释放写锁
signal(mutex);        // 释放互斥锁


 
 
3. 写者代码逻辑:


wait(write_lock); // 阻塞所有读者和写者
写入数据;  
signal(write_lock); // 释放写锁


 
 
第二类读者-写者(写者优先)
 
- 核心修改:增加一个信号量 wrt 表示写者是否等待,读者需检查是否有写者等待才能进入。
 
考点延伸
 
- 核心区别:两类问题的公平性差异,需掌握信号量增减逻辑。
 
- 适用场景:读多写少场景用读者优先,写多读少用写者优先。
 
三、哲学家就餐问题(Dining Philosophers Problem)
 
问题描述
 
- 场景:n个哲学家围坐餐桌,每人需交替“思考”和“就餐”,就餐时需同时获得左右两根筷子(资源),筷子总数等于哲学家数(每根筷子被两人共享)。
 
- 核心矛盾:若所有哲学家同时拿左侧筷子,会导致死锁(循环等待资源)。
 
关键解决方案
 
1. 避免死锁的策略:
 
- 策略1:限制同时就餐人数
 
- 用信号量 sm 限制最多 n-1 个哲学家同时拿筷子,确保至少1人不抢筷,打破循环等待。
 
- 策略2:奇数拿左筷,偶数拿右筷
 
- 强制部分哲学家先拿右筷,避免统一方向的资源竞争。
 
- 策略3:仅当两根筷子都可用时才拿取
 
- 用信号量数组 chopstick[5] 表示筷子,哲学家拿筷前先检查左右是否可用(需互斥访问状态数组)。
 
2. 基于信号量的代码示例(策略1):


semaphore chopstick[5] = {1,1,1,1,1}; // 筷子信号量
semaphore sm = n-1;                  // 限制就餐人数
void philosopher(int i) {
    while (1) {
        think();
        wait(sm);          // 等待可用就餐名额
        wait(chopstick[i]); // 拿左筷
        wait(chopstick[(i+1)%5]); // 拿右筷
        eat();
        signal(chopstick[i]); // 放左筷
        signal(chopstick[(i+1)%5]); // 放右筷
        signal(sm);          // 释放就餐名额
    }
}


 
 
考点延伸
 
- 死锁必要条件:本题是“循环等待”的典型案例,解决方案需针对死锁条件(如破坏循环等待或请求与保持)。
 
- 易错题点:未考虑筷子的互斥性(每根筷子只能被一人使用),需用信号量正确表示资源。
 
考研复习要点总结
 
1. 核心能力:
 
- 熟练用信号量( wait/signal )实现互斥与同步,区分互斥信号量(初值1)和同步信号量(初值非1)。
 
- 分析问题中的临界资源、同步关系(如“前操作未完成,后操作需等待”)。
 
2. 答题模板:
 
- 明确信号量含义及初值 → 编写进程代码 → 验证是否解决互斥/同步问题 → 分析是否存在死锁/饥饿。
 
3. 真题方向:
 
- 变种问题(如带缓冲区的读者-写者、多桌哲学家),需灵活调整信号量逻辑。

### 消息队列中消费者的分发机制 在消息队列系统中,消费者分发机制是一个重要的概念。对于 RabbitMQ 而言,其默认的消息分发方式是基于轮询调度算法实现的。这意味着当多个消费者连接到同一个队列时,RabbitMQ 会按照顺序依次将消息分发给不同的消费者[^1]。 #### 默认分发行为 RabbitMQ 的消息分发并不考虑当前消费者未确认的消息数量,而是简单地按序分配每一条新到达的消息。例如,如果有两个消费者 A 和 B,那么第一条消息会被发送给 A,第二条消息则被发送给 B,第三条再次回到 A,依此类推。 然而,在实际应用中,可能需要更复杂的逻辑来决定哪些消费者应该接收到特定类型的消息或者满足某些条件下的负载均衡需求。这就涉及到自定义路由键和绑定模式的应用场景: - **通过 Routing Key 实现筛选**: 生产者可以在发布消息时指定一个 routing key, 同时设置 exchange 类型为 direct 或 topic 。这样只有订阅了匹配该 routing key 的 queue 才能接收对应的消息[^3]。 - **优先级控制**: 如果希望部分重要程度较高的任务能够更快得到处理,则可以启用 message priority 功能 (需预先配置好支持此特性的 broker)[^4]. 至于提到的具体奇数偶数区分情况,并不属于标准 rabbitmq 行为范畴之内;如果确实存在这样的业务诉求的话,可以通过如下方法达成目标: 1. 利用 `direct` Exchange 并创建两条独立 Queue 分别用于存放 Odd/Even Numbered Messages. 2. 在 Producer 发送数据之前先判断数值属性再附加相应 Tag 至 Properties 中作为额外元信息传递下去; 3. Consumer End 可依据这些标记自行过滤不符合预期范围内的项目不予理会即可完成初步分类效果. ```python import pika connection = pika.BlockingConnection(pika.ConnectionParameters('localhost')) channel = connection.channel() def send_message(number): channel.exchange_declare(exchange='numbers', exchange_type='direct') if number % 2 == 0: routing_key = 'even' else: routing_key = 'odd' channel.basic_publish( exchange='numbers', routing_key=routing_key, body=str(number)) send_message(5) # Example sending odd number ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值