当前位置: 首页 > news >正文

经典同步问题详解

1. 生产者-消费者问题 🏭🛒

问题描述

  • 生产者生产产品放入缓冲区,消费者从缓冲区取出产品。
  • 缓冲区规则
    • 缓冲区满时,生产者等待(empty=0)。
    • 缓冲区空时,消费者等待(full=0)。
    • 访问缓冲区必须互斥(防止数据竞争)。

关键点
同步关系

  • 生产者 V(full) 通知消费者“有产品可消费”。
  • 消费者 V(empty) 通知生产者“有空位可生产”。

互斥关系

  • 通过 mutex 保证同一时间只有一个进程操作缓冲区。

⚠️ 死锁风险

  • 若先 P(mutex)P(empty),可能所有进程互相等待(如缓冲区满时生产者占 mutexempty,消费者又等 mutex)。
  • 正确顺序:先检查资源(empty/full),再锁 mutex

代码实现:

semaphore mutex = 1;   // 互斥锁,保护缓冲区
semaphore empty = n;   // 空缓冲区数量(初始=n)
semaphore full = 0;    // 满缓冲区数量(初始=0)void producer() {while (1) {生产一个产品;P(empty);      // 申请一个空位(如果没有空位,阻塞)P(mutex);      // 进入临界区(锁住缓冲区)把产品放入缓冲区;V(mutex);      // 退出临界区(释放锁)V(full);       // 增加一个满缓冲区(通知消费者)}
}void consumer() {while (1) {P(full);       // 申请一个产品(如果没有产品,阻塞)P(mutex);      // 进入临界区(锁住缓冲区)从缓冲区取出产品;V(mutex);      // 退出临界区(释放锁)V(empty);      // 增加一个空位(通知生产者)消费产品;}
}

关键点 🔑

  1. P(empty) P(full) 必须在 P(mutex) 之前
    1. 如果先 P(mutex)P(empty),可能导致死锁(生产者占着锁等空位,消费者进不去)。
  2. V(full) V(empty) 是“通知”对方
    • V(full) 告诉消费者“有数据可读”。
    • V(empty) 告诉生产者“有空位可写”。

 

2. 读者-写者问题 📖✍️

问题描述

  • 读者可共享读取文件,写者必须独占写入。
  • 要求
    • 读时允许其他读者,但禁止写者。
    • 写时禁止其他所有读写操作。

关键点
🔹 读者优先(默认方案):

  • 第一个读者锁 rw(阻止写者),最后一个读者释放 rw
  • count 记录当前读者数,用 mutex 保护 count

🔹 写者优先(公平方案):

  • 新增信号量 w,写者来时阻止新读者排队。
  • 已有读者读完后再唤醒写者。

⚠️ 问题

  • 默认方案可能导致写者饥饿(读者源源不断时写者永远等待)。

代码实现(读者优先)

int read_count = 0;     // 当前读者数量
semaphore rw_mutex = 1; // 读写锁(写者独占)
semaphore count_mutex = 1; // 保护 read_countvoid writer() {while (1) {P(rw_mutex);    // 写者独占访问写入文件;V(rw_mutex);    // 释放锁}
}void reader() {while (1) {P(count_mutex);     // 保护 read_countif (read_count == 0) P(rw_mutex);    // 第一个读者锁住写者read_count++;V(count_mutex);读取文件;  // 多个读者可同时读P(count_mutex);read_count--;if (read_count == 0)V(rw_mutex);    // 最后一个读者释放锁V(count_mutex);}
}

关键点 🔑

  1. 读者优先:只要有一个读者在读,后续读者可以直接进入,写者必须等待。
  2. read_count 需要互斥保护,否则多个读者同时修改会导致竞争。
  3. 写者必须独占 rw_mutex,保证写操作安全。

 

3. 哲学家问题 🍜🥢

问题描述

  • 5哲学家围坐,每人需左右两根筷子吃饭。
  • 死锁场景:若所有人同时拿左筷子,则无人能拿到右筷子,永远等待。

解决方案
🔸 破坏死锁条件

  1. 限制并发:最多4人同时拿筷子(至少1人能吃到)。
  2. 原子拿筷:只有左右筷子都空闲时才拿(用 mutex 保护拿筷操作)。
  3. 奇偶策略:奇数号先拿左筷,偶数号先拿右筷。

核心思想

  • 避免“贪心”行为(拿一根等一根),确保要么拿到全部资源,要么不拿

代码实现(避免死锁版)

semaphore chopstick[5] = {1, 1, 1, 1, 1}; // 5根筷子
semaphore mutex = 1;  // 保护拿筷子操作void philosopher(int i) {while (1) {P(mutex);                // 进入临界区P(chopstick[i]);         // 拿左筷子P(chopstick[(i+1)%5]);   // 拿右筷子V(mutex);               // 离开临界区吃饭;                   // 同时持有两根筷子V(chopstick[i]);         // 放回左筷子V(chopstick[(i+1)%5]);   // 放回右筷子}
}

关键点 🔑

  1. 直接拿两根筷子可能死锁(所有人同时拿左筷子,互相等待右筷子)。
  2. mutex 保证拿筷子是原子操作,避免竞争。
  3. 替代方案
    • 限制最多 4 个哲学家同时拿筷子。
    • 奇数号先拿左筷子,偶数号先拿右筷子。

 

总结 🌟

  1. 生产者-消费者问题:通过empty/full信号量实现缓冲区的同步控制,mutex保证互斥访问,注意P操作顺序避免死锁;
  2. 读者-写者问题:使用计数器记录读者数量,读写锁保证写者独占,默认方案可能导致写者饥饿;
  3. 哲学家问题:采用原子化拿筷策略或限制并发人数打破循环等待。这些案例揭示了并发编程的核心:资源同步、互斥访问与死锁预防的平衡艺术

问题

核心矛盾

关键解法

生产者-消费者

缓冲区同步+互斥

empty/full 信号量 + mutex

读者-写者

读写互斥 vs 读读共享

count计数器 + rw

哲学家

循环等待死锁

资源预判/拿筷策略

理解这些问题的核心是:同步协作 + 互斥保护 + 避免死锁! 🚀

http://www.dtcms.com/a/275248.html

相关文章:

  • 使用 lstrip() 和 rstrip() 方法
  • java集合类
  • 【牛客刷题】吃糖果----糖果甜度问题(贪心策略详解)
  • 机器学习详解
  • Windows删除文件或者拔出U盘显示正在使用/占用解决办法
  • Android tombstones memory map分析
  • HarmonyOS从入门到精通:动画设计与实现之四 - 转场动画设计与流畅交互体验
  • 优选算法 --(双指针算法 1~8)
  • The Practice of Programming
  • 深入解码 Docker 镜像与容器的奇妙世界
  • 小车循迹功能的实现(第六天)
  • 自由学习记录(68)
  • C#事件:从原理到实践的深度剖析
  • 数据结构 顺序表(3)---顺序表的应用
  • 网安学习NO.14
  • 创意总监的动态视觉秘诀:用AE动态遮罩AI,轻松实现“人景分离”
  • 分割网络Segformer
  • 需求跟踪深度解析:架构师视角下的全链路追溯体系
  • Vue性能监控
  • PreparedStatement 实现分页查询详解
  • 你以为大数据只是存?其实真正的“宝藏”藏在这招里——数据挖掘!
  • 自动评论+AI 写作+定时发布,这款媒体工具让自媒体人躺赚流量
  • 卸载软件总留一堆“垃圾”?这款免费神器,一键扫清注册表和文件残留!
  • BLOB 数据的插入与读取详解
  • 9月22日跨境电商高峰会都说了啥?郑州跨境电商发展机遇在哪?
  • Nginx的配置与使用
  • 多元思维模型:数据分析需要具备的四大能力?
  • 傅里叶方法求解正方形偏微分方程
  • Redis缓存三兄弟:穿透、击穿、雪崩全解析
  • 张量与维度