进程同步互斥

重点 生产者 - 消费者问题

问题描述

  • 生产者:生产数据放入缓冲区
  • 消费者:从缓冲区取数据消费
  • 同步关系:缓冲区满则生产者等待,缓冲区空则消费者等待
  • 互斥关系:对缓冲区的互斥访问

PV 解决方案(3 个信号量)

semaphore mutex = 1;   // 保护缓冲区互斥访问
semaphore empty = N;   // 空缓冲区数量
semaphore full  = 0;   // 满缓冲区数量
 
// 生产者
while (true) {
  produce(data);
  P(empty);       // 先申请空缓冲区(资源信号量)
  P(mutex);       // 再申请互斥访问
    put(data);
  V(mutex);
  V(full);        // 满缓冲区 +1
}
 
// 消费者
while (true) {
  P(full);
  P(mutex);
    get(data);
  V(mutex);
  V(empty);
  consume(data);
}

P 操作顺序

  • 必须先 P(资源信号量),再 P(互斥信号量)
  • 如果顺序颠倒(先 P(mutex) 再 P(empty)),满缓冲区时生产者持有 mutex 阻塞,消费者无法进入,形成死锁

重点 读者 - 写者问题

读者优先

  • 多个读者可同时读
  • 写者必须独占访问,且只能等所有读者完成
  • 使用 readcount 统计读者数,mutex 保护 readcountwrt 实现写者互斥
semaphore mutex = 1;
semaphore wrt = 1;
int readcount = 0;
 
// 读者
P(mutex);
  readcount++;
  if (readcount == 1) P(wrt);  // 第一个读者锁住写者
V(mutex);
  read;
P(mutex);
  readcount--;
  if (readcount == 0) V(wrt);  // 最后一个读者释放写者
V(mutex);
 
// 写者
P(wrt);
  write;
V(wrt);

写者优先

  • 当有写者等待时,新读者不能开始读
  • 需额外的信号量 read 来阻塞新读者

重点 哲学家就餐问题

问题描述

  • 5 个哲学家围坐,5 支筷子交替放置
  • 每个哲学家需要两支筷子才能进餐
  • 死锁风险:所有哲学家同时拿起左边筷子,导致循环等待

解决方案

  1. 限制并发人数:最多只允许 4 个哲学家同时就餐(信号量 limit = 4
  2. 奇偶拿筷:奇数号先取左边筷子,偶数号先取右边筷子,打破循环等待
  3. AND 信号量:同时申请两支筷子(一次 P 操作获取全部所需资源)

重点 管程(Monitor)

  • 将共享数据和对该数据的操作封装在一起
  • 类似 OOP 中的类:共享变量 + 方法 + 条件变量
  • 同一时间只有一个进程能进入管程,自动保证互斥
  • 使用条件变量(Condition Variable)实现同步

信号量解决思路总结

  • 互斥信号量 保护临界区
  • 资源信号量 表示缓冲区状态
  • 同步信号量协调执行顺序