当前位置: 首页 > 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.lryc.cn/news/585543.html

相关文章:

  • 液冷智算数据中心崛起,AI算力联动PC Farm与云智算开拓新蓝海(二)
  • Apache Cloudberry 向量化实践(三)重塑表达式构建路径:Gandiva 优化实战
  • 2D下的几何变换(C#实现,持续更新)
  • SpringBoot或OpenFeign中 Jackson 配置参数名蛇形、小驼峰、大驼峰、自定义命名
  • SpringCloud之Ribbon
  • BootstrapBlazor与JS互调
  • Semi-Supervised Single-View 3D Reconstruction via Prototype Shape Priors
  • 小智AI模型接入MCP
  • 【一起来学AI大模型】微调技术:LoRA(Low-Rank Adaptation) 的实战应用
  • SQL Server通过CLR连接InfluxDB实现异构数据关联查询技术指南
  • SpringBoot JWT
  • Rust与UE5高效集成实战
  • uniapp制作一个个人页面
  • ffmpeg-api记录
  • UC浏览器PC版自2016年后未再更新不支持vue3
  • 小旺AI截图1.2.1版本上线:新增录屏音频、Mac长截屏
  • Docker高级管理--Dockerfile 镜像制作
  • 手把手一起使用Miniforge3+mamba平替Anaconda(Win10)
  • 机器学习week2-线性回归加强
  • Java的extends通配符
  • netdxf—— CAD c#二次开发之(netDxf 处理 DXF 文件)
  • 和鲸社区深度学习基础训练营2025年关卡2(3)pytorch
  • 利用Claude code,只用文字版系统设计大纲,就能轻松实现系统~
  • 免费应用分发平台的安全漏洞和防护机制是什么?
  • 60 美元玩转 Li-Fi —— 开源 OpenVLC 平台入门(附 BeagleBone Black 驱动简单解析)
  • Windows解决 ping 127.0.0.1 一般故障问题
  • 【Linux网络】深入理解HTTP/HTTPS协议:原理、实现与加密机制全面解析
  • 信号量机制
  • 聊聊AI大模型的上下文工程(Context Engineering)
  • Spring 声明式事务:从原理到实现的完整解析