目录
[二、 进程调度算法](#二、 进程调度算法)
[1. 通用进程类](#1. 通用进程类)
[2. 先来先服务(FCFS, First-Come, First-Served)](#2. 先来先服务(FCFS, First-Come, First-Served))
[3. 短作业优先(SJF, Shortest-Job-First)/ 短进程优先(SPF)](#3. 短作业优先(SJF, Shortest-Job-First)/ 短进程优先(SPF))
[4. 优先级调度算法](#4. 优先级调度算法)
[5. 时间片轮转调度算法(RR, Round-Robin)](#5. 时间片轮转调度算法(RR, Round-Robin))
[6. 多级反馈队列调度算法](#6. 多级反馈队列调度算法)
[三、 进程同步与互斥算法](#三、 进程同步与互斥算法)
[1. 临界区问题的解决算法](#1. 临界区问题的解决算法)
[软件实现算法:Peterson 算法](#软件实现算法:Peterson 算法)
硬件实现方法:Test-and-Set(TS)指令(原子操作)
[硬件实现方法:Swap 指令(原子交换)](#硬件实现方法:Swap 指令(原子交换))
[2. 信号量与 PV 操作](#2. 信号量与 PV 操作)
[生产者 - 消费者问题](#生产者 - 消费者问题)
[读者 - 写者问题](#读者 - 写者问题)
[3. 管程(Monitor)](#3. 管程(Monitor))
[四、 死锁处理算法](#四、 死锁处理算法)
[1. 死锁预防算法](#1. 死锁预防算法)
[算法 1:破坏 "请求与保持"(一次性申请所有资源)](#算法 1:破坏 “请求与保持”(一次性申请所有资源))
[算法 2:破坏 "不剥夺"(申请失败则释放已占资源)](#算法 2:破坏 “不剥夺”(申请失败则释放已占资源))
[算法 3:破坏 "循环等待"(资源升序申请)](#算法 3:破坏 “循环等待”(资源升序申请))
[2. 死锁避免算法](#2. 死锁避免算法)
[3. 死锁检测与解除算法](#3. 死锁检测与解除算法)
[五、 各类算法的对比与选型](#五、 各类算法的对比与选型)
一、引言
操作系统中与进程 相关的核心算法,主要分为进程调度算法 、进程同步与互斥算法 、死锁处理算法三大类,这些算法是保障操作系统高效、稳定运行的关键。下面结合原理、特点和适用场景进行详细讲解,以及Python代码完整实现。
二、 进程调度算法
进程调度算法的核心目标是合理分配 CPU 资源 ,提高系统吞吐量、缩短响应时间、保证公平性或满足实时性要求。根据调度层次可分为作业调度(高级调度) 、进程调度(低级调度) 和 中级调度 ,其中进程调度算法是最核心的部分。
1. 通用进程类
我们先定义通用进程类Process,放在
中,与进程调度算法在同一目录下。
Python代码
python
class Process:
def __init__(self, pid, arrival_time, burst_time, priority=0):
self.pid = pid # 进程ID
self.arrival_time = arrival_time # 到达时间
self.burst_time = burst_time # 总运行时间
self.remaining_time = burst_time # 剩余运行时间(用于抢占式/轮转调度)
self.priority = priority # 优先级(数值越小优先级越高)
self.start_time = -1 # 开始执行时间
self.completion_time = -1 # 完成时间
self.turnaround_time = 0 # 周转时间 = 完成时间 - 到达时间
self.waiting_time = 0 # 等待时间 = 周转时间 - 运行时间
def __repr__(self):
return f"Process(pid={self.pid}, arrival={self.arrival_time}, burst={self.burst_time})"
2. 先来先服务(FCFS, First-Come, First-Served)
- 原理:按照进程到达就绪队列的先后顺序分配 CPU,先到的进程先执行,直到完成或阻塞才释放 CPU。
- 特点
- 优点:算法简单、公平,易于实现。
- 缺点:对短进程不友好,短进程可能被长进程 "饥饿";平均周转时间较长;不利于交互式系统。
- 适用场景:CPU 繁忙型(计算密集型)进程的批处理系统。
Python代码
python
from process import Process
def fcfs_scheduling(processes):
"""先来先服务调度算法(非抢占式)"""
# 按到达时间排序(相同到达时间按PID排序)
sorted_processes = sorted(processes, key=lambda p: (p.arrival_time, p.pid))
current_time = 0
for p in sorted_processes:
# 如果当前时间小于进程到达时间,等待到进程到达
if current_time < p.arrival_time:
current_time = p.arrival_time
# 记录开始时间
p.start_time = current_time
# 执行进程(累加运行时间)
current_time += p.burst_time
# 记录完成时间
p.completion_time = current_time
# 计算周转时间和等待时间
p.turnaround_time = p.completion_time - p.arrival_time
p.waiting_time = p.turnaround_time - p.burst_time
return sorted_processes
# 测试FCFS
if __name__ == "__main__":
processes = [
Process(1, 0, 5),
Process(2, 1, 3),
Process(3, 2, 1)
]
fcfs_result = fcfs_scheduling(processes)
print("FCFS调度结果:")
for p in fcfs_result:
print(f"进程{p.pid} | 完成时间:{p.completion_time} | 周转时间:{p.turnaround_time} | 等待时间:{p.waiting_time}")
程序运行结果展示
bash
FCFS调度结果:
进程1 | 完成时间:5 | 周转时间:5 | 等待时间:0
进程2 | 完成时间:8 | 周转时间:7 | 等待时间:4
进程3 | 完成时间:9 | 周转时间:7 | 等待时间:6
3. 短作业优先(SJF, Shortest-Job-First)/ 短进程优先(SPF)
- 原理 :优先选择预计运行时间最短 的进程投入运行。分为非抢占式 SJF (进程一旦运行就直到结束)和抢占式 SJF(最短剩余时间优先,SRTF)。
- 特点
- 优点:能有效缩短平均周转时间,提高系统吞吐量。
- 缺点:需要预知进程的运行时间,实际中很难精准获取;对长进程不友好,可能导致长进程饥饿;无法保证实时性。
- 适用场景:批处理系统,且进程运行时间可预估的场景。
Python代码
python
from process import Process
def sjf_scheduling(processes):
"""短作业优先调度算法(非抢占式)"""
remaining_processes = processes.copy()
completed_processes = []
current_time = 0
while remaining_processes:
# 筛选当前时间已到达的进程
available = [p for p in remaining_processes if p.arrival_time <= current_time]
if not available:
# 无可用进程,时间推进到下一个进程的到达时间
current_time = min(p.arrival_time for p in remaining_processes)
continue
# 选择剩余运行时间最短的进程(短作业优先)
selected = min(available, key=lambda p: p.burst_time)
# 记录开始时间
if selected.start_time == -1:
selected.start_time = current_time
# 执行进程
current_time += selected.burst_time
# 计算属性
selected.completion_time = current_time
selected.turnaround_time = selected.completion_time - selected.arrival_time
selected.waiting_time = selected.turnaround_time - selected.burst_time
# 移到完成列表
remaining_processes.remove(selected)
completed_processes.append(selected)
return completed_processes
# 测试SJF
if __name__ == "__main__":
processes = [
Process(1, 0, 5),
Process(2, 1, 3),
Process(3, 2, 1)
]
sjf_result = sjf_scheduling(processes)
print("SJF调度结果:")
for p in sjf_result:
print(f"进程{p.pid} | 完成时间:{p.completion_time} | 周转时间:{p.turnaround_time} | 等待时间:{p.waiting_time}")
程序运行结果展示
bash
SJF调度结果:
进程1 | 完成时间:5 | 周转时间:5 | 等待时间:0
进程3 | 完成时间:6 | 周转时间:4 | 等待时间:3
进程2 | 完成时间:9 | 周转时间:8 | 等待时间:5
4. 优先级调度算法
- 原理 :为每个进程分配一个优先级,CPU 优先分配给优先级最高的进程。分为非抢占式 (高优先级进程需等当前进程结束 / 阻塞)和抢占式(高优先级进程可打断低优先级进程)。
- 优先级分类
- 静态优先级:进程创建时确定,运行过程中不变(实现简单,但不够灵活)。
- 动态优先级:进程运行过程中动态调整(如长进程优先级随时间提升,避免饥饿)。
- 特点
- 优点:可以满足紧急任务的需求,支持实时系统。
- 缺点:低优先级进程可能长期饥饿;优先级设置不合理会导致系统性能下降。
- 适用场景:实时系统(如工业控制、医疗设备)、需要区分任务紧急程度的系统。
Python代码
python
from process import Process
def priority_scheduling(processes):
"""优先级调度算法(非抢占式,数值越小优先级越高)"""
remaining_processes = processes.copy()
completed_processes = []
current_time = 0
while remaining_processes:
# 筛选当前时间已到达的进程
available = [p for p in remaining_processes if p.arrival_time <= current_time]
if not available:
current_time = min(p.arrival_time for p in remaining_processes)
continue
# 选择优先级最高的进程
selected = min(available, key=lambda p: p.priority)
if selected.start_time == -1:
selected.start_time = current_time
# 执行进程
current_time += selected.burst_time
# 计算属性
selected.completion_time = current_time
selected.turnaround_time = selected.completion_time - selected.arrival_time
selected.waiting_time = selected.turnaround_time - selected.burst_time
remaining_processes.remove(selected)
completed_processes.append(selected)
return completed_processes
# 测试优先级调度
if __name__ == "__main__":
processes = [
Process(1, 0, 5, priority=3),
Process(2, 1, 3, priority=1),
Process(3, 2, 1, priority=2)
]
priority_result = priority_scheduling(processes)
print("优先级调度结果:")
for p in priority_result:
print(f"进程{p.pid} | 优先级:{p.priority} | 完成时间:{p.completion_time} | 等待时间:{p.waiting_time}")
程序运行结果展示
bash
优先级调度结果:
进程1 | 优先级:3 | 完成时间:5 | 等待时间:0
进程2 | 优先级:1 | 完成时间:8 | 等待时间:4
进程3 | 优先级:2 | 完成时间:9 | 等待时间:6
5. 时间片轮转调度算法(RR, Round-Robin)
- 原理 :将 CPU 的时间划分为固定长度的时间片(如 10ms),就绪队列中的进程轮流占用 CPU,每次只使用一个时间片,时间片用完后主动释放 CPU,回到就绪队列尾部等待下一次调度。
- 关键参数:时间片长度 ------ 过长会退化为 FCFS,过短会增加进程切换开销。
- 特点
- 优点:响应时间短,公平性好,适合交互式系统(如 Windows、Linux 桌面)。
- 缺点:系统开销较大(频繁进程切换);对 CPU 密集型进程效率不高。
- 适用场景:分时操作系统,多用户交互式场景。
Python代码
python
from process import Process
def rr_scheduling(processes, time_quantum=2):
"""时间片轮转调度算法"""
from collections import deque
# 按到达时间排序初始化就绪队列
sorted_processes = sorted(processes, key=lambda p: (p.arrival_time, p.pid))
ready_queue = deque()
completed_processes = []
current_time = 0
index = 0 # 用于遍历未加入队列的进程
while index < len(sorted_processes) or ready_queue:
# 将到达时间<=当前时间的进程加入就绪队列
while index < len(sorted_processes) and sorted_processes[index].arrival_time <= current_time:
ready_queue.append(sorted_processes[index])
index += 1
if not ready_queue:
current_time = sorted_processes[index].arrival_time
continue
# 取出队首进程执行一个时间片
current_process = ready_queue.popleft()
# 记录首次执行时间
if current_process.start_time == -1:
current_process.start_time = current_time
# 执行时间片(取剩余时间和时间片的较小值)
execute_time = min(time_quantum, current_process.remaining_time)
current_time += execute_time
current_process.remaining_time -= execute_time
# 如果进程完成
if current_process.remaining_time == 0:
current_process.completion_time = current_time
current_process.turnaround_time = current_process.completion_time - current_process.arrival_time
current_process.waiting_time = current_process.turnaround_time - current_process.burst_time
completed_processes.append(current_process)
else:
# 未完成则放回队列尾部
ready_queue.append(current_process)
return completed_processes
# 测试RR
if __name__ == "__main__":
processes = [
Process(1, 0, 5),
Process(2, 1, 3),
Process(3, 2, 1)
]
rr_result = rr_scheduling(processes, time_quantum=2)
print("RR调度结果(时间片=2):")
for p in rr_result:
print(f"进程{p.pid} | 完成时间:{p.completion_time} | 周转时间:{p.turnaround_time} | 等待时间:{p.waiting_time}")
程序运行结果展示
bash
RR调度结果(时间片=2):
进程3 | 完成时间:7 | 周转时间:5 | 等待时间:4
进程1 | 完成时间:8 | 周转时间:8 | 等待时间:3
进程2 | 完成时间:9 | 周转时间:8 | 等待时间:5
6. 多级反馈队列调度算法
- 原理 :综合了 RR 和优先级调度的优点,是目前公认的最优进程调度算法之一 。
- 设置多个就绪队列,每个队列的优先级从高到低,时间片长度从短到长(如第 1 队列时间片 10ms,第 2 队列 20ms,依次翻倍)。
- 新进程进入第 1 队列,按 RR 调度;时间片用完未完成,则降到下一级队列尾部。
- 高优先级队列的进程可抢占低优先级队列的 CPU。
- 特点
- 优点:兼顾短进程(快速响应)、长进程(不会饥饿)、实时进程(高优先级);灵活性强。
- 缺点:实现复杂,需要维护多个队列和优先级。
- 适用场景:通用操作系统(如 Linux、UNIX),兼顾分时和批处理需求。
Python代码
python
from process import Process
def mlfq_scheduling(processes, queue_quantums=[2, 4, 8]):
"""多级反馈队列调度算法"""
from collections import deque
# 初始化多级队列(优先级从高到低,队列索引越小优先级越高)
queues = [deque() for _ in queue_quantums]
completed_processes = []
current_time = 0
index = 0 # 遍历未加入队列的进程
# 记录进程所在的队列级别(初始为-1,表示未加入任何队列)
process_queue_level = {p.pid: -1 for p in processes}
while index < len(processes) or any(queues):
# 将到达时间<=当前时间的进程加入最高优先级队列(队列0)
while index < len(processes) and processes[index].arrival_time <= current_time:
p = processes[index]
queues[0].append(p)
process_queue_level[p.pid] = 0
index += 1
# 从高优先级到低优先级遍历队列,找到第一个非空队列
selected_queue_idx = -1
for i in range(len(queues)):
if queues[i]:
selected_queue_idx = i
break
if selected_queue_idx == -1:
# 所有队列都空,推进时间
current_time = min(p.arrival_time for p in processes[index:])
continue
# 取出当前队列的队首进程
current_process = queues[selected_queue_idx].popleft()
if current_process.start_time == -1:
current_process.start_time = current_time
# 执行当前队列的时间片
time_quantum = queue_quantums[selected_queue_idx]
execute_time = min(time_quantum, current_process.remaining_time)
current_time += execute_time
current_process.remaining_time -= execute_time
# 判断进程是否完成
if current_process.remaining_time == 0:
current_process.completion_time = current_time
current_process.turnaround_time = current_process.completion_time - current_process.arrival_time
current_process.waiting_time = current_process.turnaround_time - current_process.burst_time
completed_processes.append(current_process)
else:
# 未完成且不是最低优先级队列,降级到下一级队列
if selected_queue_idx < len(queues) - 1:
next_level = selected_queue_idx + 1
queues[next_level].append(current_process)
process_queue_level[current_process.pid] = next_level
else:
# 最低优先级队列,放回原队列
queues[selected_queue_idx].append(current_process)
return completed_processes
# 测试多级反馈队列
if __name__ == "__main__":
processes = [
Process(1, 0, 8), # 长进程
Process(2, 1, 2), # 短进程
Process(3, 2, 1) # 极短进程
]
mlfq_result = mlfq_scheduling(processes)
print("多级反馈队列调度结果:")
for p in mlfq_result:
print(f"进程{p.pid} | 完成时间:{p.completion_time} | 周转时间:{p.turnaround_time} | 等待时间:{p.waiting_time}")
程序运行结果展示
bash
多级反馈队列调度结果:
进程2 | 完成时间:4 | 周转时间:3 | 等待时间:1
进程3 | 完成时间:5 | 周转时间:3 | 等待时间:2
进程1 | 完成时间:11 | 周转时间:11 | 等待时间:3
三、 进程同步与互斥算法
进程同步与互斥的核心是解决临界区问题 (多个进程对共享资源的互斥访问)和同步问题(进程间的协作与顺序执行)。
1. 临界区问题的解决算法
临界区问题需要满足 3 个条件:互斥性 (同一时刻只有一个进程在临界区)、空闲让进 (临界区空闲时允许进程进入)、有限等待(进程等待进入临界区的时间有限)。
- 软件实现算法
- Peterson 算法 :适用于两个进程的互斥,通过
turn(轮到哪个进程)和flag(进程是否想进入临界区)两个变量实现,满足临界区的 3 个条件。 - 面包店算法:适用于多个进程的互斥,模拟面包店取号排队的机制,实现复杂但支持任意数量进程。
- Peterson 算法 :适用于两个进程的互斥,通过
- 硬件实现方法
- 关中断:进程进入临界区前关闭中断,避免进程切换,简单但会导致系统效率下降(单核有效)。
- Test-and-Set 指令(TS 指令):原子操作,通过指令判断并设置锁变量,实现互斥。
- Swap 指令:原子交换两个变量的值,与 TS 指令功能类似。
软件实现算法:Peterson 算法
Peterson 算法的目标是解决两个进程的临界区互斥问题,且必须满足临界区的三个核心条件:
| 条件 | 含义 |
|---|---|
| 互斥性 | 同一时刻,只有一个进程能进入临界区 |
| 空闲让进 | 若临界区空闲(无进程执行),任何想进入的进程都能立即进入 |
| 有限等待 | 进程等待进入临界区的时间是有限的,不会出现 "饥饿"(无限等待)的情况 |
它是最简单的、能同时满足这三个条件的软件互斥算法,仅需两个核心变量就能实现,无需硬件支持。
Python代码
python
import threading
import time
# Peterson算法的共享变量
flag = [False, False] # 标记进程是否想进入临界区
turn = 0 # 轮到哪个进程
# 临界区资源
critical_resource = 0
def peterson_process(process_id):
global critical_resource, flag, turn
other = 1 - process_id # 另一个进程的ID
for _ in range(3): # 每个进程尝试进入临界区3次
# 1. 声明想要进入临界区
flag[process_id] = True
# 2. 礼让对方
turn = other
# 3. 等待:对方不想进 或 轮到自己
while flag[other] and turn == other:
time.sleep(0.01) # 忙等
# 临界区
print(f"进程{process_id}进入临界区,当前资源值:{critical_resource}")
critical_resource += 1
time.sleep(0.1) # 模拟临界区操作
print(f"进程{process_id}退出临界区,资源值变为:{critical_resource}")
# 4. 声明退出临界区
flag[process_id] = False
# 剩余区
time.sleep(0.1)
# 测试Peterson算法
if __name__ == "__main__":
t0 = threading.Thread(target=peterson_process, args=(0,))
t1 = threading.Thread(target=peterson_process, args=(1,))
t0.start()
t1.start()
t0.join()
t1.join()
print(f"最终临界区资源值:{critical_resource}")
程序运行结果展示
bash
进程0进入临界区,当前资源值:0
进程0退出临界区,资源值变为:1
进程1进入临界区,当前资源值:1
进程1退出临界区,资源值变为:2
进程0进入临界区,当前资源值:2
进程0退出临界区,资源值变为:3
进程1进入临界区,当前资源值:3
进程1退出临界区,资源值变为:4
进程0进入临界区,当前资源值:4
进程0退出临界区,资源值变为:5
进程1进入临界区,当前资源值:5
进程1退出临界区,资源值变为:6
最终临界区资源值:6
软件实现算法:面包店算法
面包店算法是软件实现的多进程互斥算法,模拟面包店 "取号 - 排队" 机制,满足临界区的三个核心条件(互斥性、空闲让进、有限等待):
- 每个线程进入临界区前,先 "取号"(
number[tid] = max(number) + 1); - 取号时用
choosing[tid]标记 "正在取号",避免多个线程同时取到相同的号; - 取号后,线程检查所有其他线程:只有当自己的号是 "最小号"(或同号但 ID 更小),才能进入临界区;
- 退出临界区时,释放自己的号(
number[tid] = 0)。
Python代码
python
import threading
import time
import random
# 全局共享资源(临界区保护对象)
critical_resource = 0
# 线程数(模拟多进程)
N_THREADS = 4
# 每个线程执行临界区操作的次数
OP_COUNT = 3
# 面包店算法核心变量
choosing = [False] * N_THREADS # 标记线程是否正在取号
number = [0] * N_THREADS # 存储每个线程的取号
def bakery_algorithm(tid):
"""面包店算法的临界区进入/退出逻辑"""
global critical_resource
for _ in range(OP_COUNT):
# ---------------------- 进入区:取号 + 排队 ----------------------
choosing[tid] = True # 标记:开始取号
# 取号:当前最大号 + 1(保证唯一性)
number[tid] = max(number) + 1
choosing[tid] = False # 标记:取号完成
# 等待:检查所有其他线程,确认自己是最小号
for other_tid in range(N_THREADS):
# 跳过自己
if other_tid == tid:
continue
# 等待条件:
# 1. 其他线程正在取号 → 等它取完
# 2. 其他线程的号更小,或同号但ID更小 → 等它执行完
while choosing[other_tid] or (number[other_tid] != 0 and
(number[other_tid] < number[tid] or
(number[other_tid] == number[tid] and other_tid < tid))):
time.sleep(0.001) # 忙等
# ---------------------- 临界区 ----------------------
print(f"🔒 线程{tid}进入临界区,资源值:{critical_resource}")
temp = critical_resource
time.sleep(0.1) # 模拟临界区操作(故意加延迟,测试互斥)
critical_resource = temp + 1
print(f"🔓 线程{tid}退出临界区,资源值:{critical_resource}")
# ---------------------- 退出区:释放号 ----------------------
number[tid] = 0
# 剩余区:模拟非临界区操作
time.sleep(random.uniform(0.05, 0.1))
# 测试面包店算法
def test_bakery():
global critical_resource, choosing, number
critical_resource = 0
choosing = [False] * N_THREADS
number = [0] * N_THREADS
threads = [threading.Thread(target=bakery_algorithm, args=(i,)) for i in range(N_THREADS)]
print("===== 面包店算法测试 =====")
for t in threads:
t.start()
for t in threads:
t.join()
print(f"最终共享资源值:{critical_resource}(预期:{N_THREADS * OP_COUNT})\n")
# 执行测试
test_bakery()
程序运行结果展示
bash
===== 面包店算法测试 =====
🔒 线程0进入临界区,资源值:0
🔓 线程0退出临界区,资源值:1
🔒 线程1进入临界区,资源值:1
🔓 线程1退出临界区,资源值:2
🔒 线程2进入临界区,资源值:2
🔓 线程2退出临界区,资源值:3
🔒 线程3进入临界区,资源值:3
🔓 线程3退出临界区,资源值:4
🔒 线程0进入临界区,资源值:4
🔓 线程0退出临界区,资源值:5
🔒 线程1进入临界区,资源值:5
🔓 线程1退出临界区,资源值:6
🔒 线程2进入临界区,资源值:6
🔓 线程2退出临界区,资源值:7
🔒 线程3进入临界区,资源值:7
🔓 线程3退出临界区,资源值:8
🔒 线程0进入临界区,资源值:8
🔓 线程0退出临界区,资源值:9
🔒 线程1进入临界区,资源值:9
🔓 线程1退出临界区,资源值:10
🔒 线程2进入临界区,资源值:10
🔓 线程2退出临界区,资源值:11
🔒 线程3进入临界区,资源值:11
🔓 线程3退出临界区,资源值:12
最终共享资源值:12(预期:12)
硬件实现方法:关中断
关中断是单核 CPU 下的简单互斥方法:
- 进程进入临界区前,关闭 CPU 中断(屏蔽时钟中断);
- 因为没有中断,CPU 不会切换进程,临界区代码独占执行;
- 退出临界区后,打开中断,恢复进程调度。⚠️ 缺点:单核有效,多核失效;关中断时间过长会导致系统响应慢、效率下降。
Python 无法真正控制 CPU 中断,用线程锁模拟关中断(获取锁 = 关中断,释放锁 = 开中断),体现核心逻辑:
python
import threading
import time
import random
# 全局共享资源(临界区保护对象)
critical_resource = 0
# 线程数(模拟多进程)
N_THREADS = 4
# 每个线程执行临界区操作的次数
OP_COUNT = 3
# 模拟"中断开关"(单核下全局唯一)
interrupt_locked = threading.Lock()
def disable_interrupts(tid):
"""模拟关中断的互斥逻辑"""
global critical_resource
for _ in range(OP_COUNT):
# ---------------------- 进入区:关中断 ----------------------
interrupt_locked.acquire() # 关中断:独占CPU,不切换线程
print(f"🚫 线程{tid}关闭中断,进入临界区,资源值:{critical_resource}")
# ---------------------- 临界区 ----------------------
temp = critical_resource
time.sleep(0.1)
critical_resource = temp + 1
# ---------------------- 退出区:开中断 ----------------------
print(f"✅ 线程{tid}打开中断,退出临界区,资源值:{critical_resource}")
interrupt_locked.release() # 开中断:恢复线程调度
# 剩余区
time.sleep(random.uniform(0.05, 0.1))
# 测试关中断
def test_disable_interrupts():
global critical_resource
critical_resource = 0
threads = [threading.Thread(target=disable_interrupts, args=(i,)) for i in range(N_THREADS)]
print("===== 关中断(模拟)测试 =====")
for t in threads:
t.start()
for t in threads:
t.join()
print(f"最终共享资源值:{critical_resource}(预期:{N_THREADS * OP_COUNT})\n")
# 执行测试
test_disable_interrupts()
程序运行结果展示
bash
===== 关中断(模拟)测试 =====
🚫 线程0关闭中断,进入临界区,资源值:0
✅ 线程0打开中断,退出临界区,资源值:1
🚫 线程1关闭中断,进入临界区,资源值:1
✅ 线程1打开中断,退出临界区,资源值:2
🚫 线程2关闭中断,进入临界区,资源值:2
✅ 线程2打开中断,退出临界区,资源值:3
🚫 线程3关闭中断,进入临界区,资源值:3
✅ 线程3打开中断,退出临界区,资源值:4
🚫 线程0关闭中断,进入临界区,资源值:4
✅ 线程0打开中断,退出临界区,资源值:5
🚫 线程1关闭中断,进入临界区,资源值:5
✅ 线程1打开中断,退出临界区,资源值:6
🚫 线程2关闭中断,进入临界区,资源值:6
✅ 线程2打开中断,退出临界区,资源值:7
🚫 线程3关闭中断,进入临界区,资源值:7
✅ 线程3打开中断,退出临界区,资源值:8
🚫 线程0关闭中断,进入临界区,资源值:8
✅ 线程0打开中断,退出临界区,资源值:9
🚫 线程1关闭中断,进入临界区,资源值:9
✅ 线程1打开中断,退出临界区,资源值:10
🚫 线程2关闭中断,进入临界区,资源值:10
✅ 线程2打开中断,退出临界区,资源值:11
🚫 线程3关闭中断,进入临界区,资源值:11
✅ 线程3打开中断,退出临界区,资源值:12
最终共享资源值:12(预期:12)
硬件实现方法:Test-and-Set(TS)指令(原子操作)
Test-and-Set 是硬件提供的原子指令,执行逻辑不可分割,核心伪代码:
python
def TestAndSet(lock):
old_value = lock # 读取锁的当前值
lock = True # 无条件设置锁为"占用"
return old_value # 返回旧值
- 调用 TS 指令时,若返回
False:锁空闲,当前进程获取锁,进入临界区; - 若返回
True:锁被占用,进程循环调用 TS(忙等),直到获取锁。
用 Python 的threading.Lock保证 TS 操作的原子性(硬件指令本身是原子的):
python
import threading
import time
import random
# 全局共享资源(临界区保护对象)
critical_resource = 0
# 线程数(模拟多进程)
N_THREADS = 4
# 每个线程执行临界区操作的次数
OP_COUNT = 3
# TS指令的共享锁(初始为False:空闲)
ts_lock = False
# 保证TS操作原子性的底层锁(模拟硬件原子指令)
ts_atomic_lock = threading.Lock()
def TestAndSet():
"""模拟TS原子指令"""
global ts_lock
with ts_atomic_lock: # 保证以下操作原子性
old_value = ts_lock
ts_lock = True
return old_value
def release_lock():
"""释放TS锁(原子操作)"""
global ts_lock
with ts_atomic_lock:
ts_lock = False
def ts_algorithm(tid):
"""基于TS指令的互斥逻辑"""
global critical_resource
for _ in range(OP_COUNT):
# ---------------------- 进入区:忙等获取TS锁 ----------------------
while TestAndSet(): # TS返回True → 锁被占用,继续等
time.sleep(0.001)
# ---------------------- 临界区 ----------------------
print(f"🔑 线程{tid}通过TS获取锁,进入临界区,资源值:{critical_resource}")
temp = critical_resource
time.sleep(0.1)
critical_resource = temp + 1
print(f"🔐 线程{tid}退出临界区,释放TS锁,资源值:{critical_resource}")
# ---------------------- 退出区:释放锁 ----------------------
release_lock()
# 剩余区
time.sleep(random.uniform(0.05, 0.1))
# 测试TS指令
def test_ts_algorithm():
global critical_resource, ts_lock
critical_resource = 0
ts_lock = False
threads = [threading.Thread(target=ts_algorithm, args=(i,)) for i in range(N_THREADS)]
print("===== Test-and-Set指令测试 =====")
for t in threads:
t.start()
for t in threads:
t.join()
print(f"最终共享资源值:{critical_resource}(预期:{N_THREADS * OP_COUNT})\n")
# 执行测试
test_ts_algorithm()
程序运行结果展示
bash
===== Test-and-Set指令测试 =====
🔑 线程0通过TS获取锁,进入临界区,资源值:0
🔐 线程0退出临界区,释放TS锁,资源值:1
🔑 线程3通过TS获取锁,进入临界区,资源值:1
🔐 线程3退出临界区,释放TS锁,资源值:2
🔑 线程2通过TS获取锁,进入临界区,资源值:2
🔐 线程2退出临界区,释放TS锁,资源值:3
🔑 线程1通过TS获取锁,进入临界区,资源值:3
🔐 线程1退出临界区,释放TS锁,资源值:4
🔑 线程0通过TS获取锁,进入临界区,资源值:4
🔐 线程0退出临界区,释放TS锁,资源值:5
🔑 线程2通过TS获取锁,进入临界区,资源值:5
🔐 线程2退出临界区,释放TS锁,资源值:6
🔑 线程0通过TS获取锁,进入临界区,资源值:6
🔐 线程0退出临界区,释放TS锁,资源值:7
🔑 线程2通过TS获取锁,进入临界区,资源值:7
🔐 线程2退出临界区,释放TS锁,资源值:8
🔑 线程3通过TS获取锁,进入临界区,资源值:8
🔐 线程3退出临界区,释放TS锁,资源值:9
🔑 线程1通过TS获取锁,进入临界区,资源值:9
🔐 线程1退出临界区,释放TS锁,资源值:10
🔑 线程3通过TS获取锁,进入临界区,资源值:10
🔐 线程3退出临界区,释放TS锁,资源值:11
🔑 线程1通过TS获取锁,进入临界区,资源值:11
🔐 线程1退出临界区,释放TS锁,资源值:12
最终共享资源值:12(预期:12)
硬件实现方法:Swap 指令(原子交换)
Swap(交换)也是硬件原子指令,原子交换两个变量的值,核心伪代码:
python
def Swap(a, b):
temp = a
a = b
b = temp
互斥实现逻辑:
- 共享锁变量
lock(初始False:空闲); - 进程私有变量
key(设为True:表示 "想要锁"); - 原子交换
key和lock:- 若交换后
key=False:说明锁空闲,进程获取锁; - 若交换后
key=True:说明锁被占用,循环交换直到key=False。
- 若交换后
Python代码
python
import threading
import time
import random
# 全局共享资源(临界区保护对象)
critical_resource = 0
# 线程数(模拟多进程)
N_THREADS = 4
# 每个线程执行临界区操作的次数
OP_COUNT = 3
# 用单元素列表存储swap_lock,实现"引用"效果(可变容器)
swap_lock = [False] # [False] = 空闲,[True] = 占用
# 保证Swap原子性的底层锁
swap_atomic_lock = threading.Lock()
def Swap(a, b):
"""模拟原子Swap指令(交换两个变量的值)"""
with swap_atomic_lock:
temp = a[0]
a[0] = b[0]
b[0] = temp
def swap_algorithm(tid):
"""基于Swap指令的互斥逻辑"""
global critical_resource
for _ in range(OP_COUNT):
# ---------------------- 进入区:忙等获取锁 ----------------------
key = [True] # 进程私有变量(想要获取锁)
while key[0]:
# 每次循环都直接操作全局swap_lock的引用
Swap(key, swap_lock)
if key[0]: # 交换后key=True → 锁被占用,忙等
time.sleep(0.001) # 减少CPU消耗
# ---------------------- 临界区 ----------------------
print(f"🔄 线程{tid}通过Swap获取锁,进入临界区,资源值:{critical_resource}")
temp = critical_resource
time.sleep(0.1)
critical_resource = temp + 1
print(f"🔁 线程{tid}退出临界区,释放Swap锁,资源值:{critical_resource}")
# ---------------------- 退出区:原子释放锁 ----------------------
with swap_atomic_lock:
swap_lock[0] = False # 原子释放锁
# 剩余区
time.sleep(random.uniform(0.05, 0.1))
# 测试Swap指令
def test_swap_algorithm():
global critical_resource, swap_lock
critical_resource = 0
swap_lock[0] = False # 重置锁状态
threads = [threading.Thread(target=swap_algorithm, args=(i,)) for i in range(N_THREADS)]
print("===== Swap指令测试 =====")
for t in threads:
t.start()
for t in threads:
t.join()
print(f"最终共享资源值:{critical_resource}(预期:{N_THREADS * OP_COUNT})\n")
# 执行测试
test_swap_algorithm()
程序运行结果展示
bash
===== Swap指令测试 =====
🔄 线程0通过Swap获取锁,进入临界区,资源值:0
🔁 线程0退出临界区,释放Swap锁,资源值:1
🔄 线程1通过Swap获取锁,进入临界区,资源值:1
🔁 线程1退出临界区,释放Swap锁,资源值:2
🔄 线程2通过Swap获取锁,进入临界区,资源值:2
🔁 线程2退出临界区,释放Swap锁,资源值:3
🔄 线程3通过Swap获取锁,进入临界区,资源值:3
🔁 线程3退出临界区,释放Swap锁,资源值:4
🔄 线程0通过Swap获取锁,进入临界区,资源值:4
🔁 线程0退出临界区,释放Swap锁,资源值:5
🔄 线程2通过Swap获取锁,进入临界区,资源值:5
🔁 线程2退出临界区,释放Swap锁,资源值:6
🔄 线程1通过Swap获取锁,进入临界区,资源值:6
🔁 线程1退出临界区,释放Swap锁,资源值:7
🔄 线程2通过Swap获取锁,进入临界区,资源值:7
🔁 线程2退出临界区,释放Swap锁,资源值:8
🔄 线程1通过Swap获取锁,进入临界区,资源值:8
🔁 线程1退出临界区,释放Swap锁,资源值:9
🔄 线程3通过Swap获取锁,进入临界区,资源值:9
🔁 线程3退出临界区,释放Swap锁,资源值:10
🔄 线程0通过Swap获取锁,进入临界区,资源值:10
🔁 线程0退出临界区,释放Swap锁,资源值:11
🔄 线程3通过Swap获取锁,进入临界区,资源值:11
🔁 线程3退出临界区,释放Swap锁,资源值:12
最终共享资源值:12(预期:12)
2. 信号量与 PV 操作
信号量是解决进程同步与互斥的最常用工具,由 Dijkstra 提出。
- 原理 :信号量
S是一个整型变量,只能通过P操作(减 1,申请资源)和V操作(加 1,释放资源)访问,且这两个操作是原子操作。 - 互斥实现 :设置互斥信号量
mutex=1,进程进入临界区前执行P(mutex),退出时执行V(mutex)。 - 同步实现 :设置同步信号量
S=0,进程 A 完成任务后执行V(S),进程 B 等待任务完成时执行P(S),实现 A→B 的顺序执行。 - 经典应用:生产者 - 消费者问题、读者 - 写者问题、哲学家进餐问题。
基础准备:信号量类封装
首先写出信号量类Semaphore,放在
中,并与后面的三个经典问题的代码文件位于同一目录下,确保 PV 操作的原子性,这是实现三个经典问题的基础:
python
import threading
import time
class Semaphore:
"""信号量类,实现原子性的P/V操作"""
def __init__(self, value=0):
self.value = value
self.lock = threading.Lock() # 保证PV操作原子性
self.condition = threading.Condition(self.lock)
def P(self):
"""申请资源(减1),若资源不足则阻塞"""
with self.condition:
while self.value <= 0:
self.condition.wait() # 阻塞等待资源
self.value -= 1
def V(self):
"""释放资源(加1),唤醒等待的进程/线程"""
with self.condition:
self.value += 1
self.condition.notify() # 唤醒一个等待的线程
生产者 - 消费者问题
问题背景
- 生产者进程:持续生产产品,放入有限大小的缓冲区;
- 消费者进程:持续从缓冲区取出产品消费;
- 核心约束:
- 缓冲区满时,生产者不能生产(同步约束);
- 缓冲区空时,消费者不能消费(同步约束);
- 同一时刻,只有一个进程(生产者 / 消费者)操作缓冲区(互斥约束)。
信号量设计
| 信号量 | 初始值 | 作用 |
|---|---|---|
empty |
缓冲区大小 N | 表示空缓冲区的数量,生产者生产前需申请(P 操作),满时阻塞生产者; |
full |
0 | 表示满缓冲区的数量,消费者消费前需申请(P 操作),空时阻塞消费者; |
mutex |
1 | 互斥信号量,保护缓冲区的临界区操作(避免多进程同时修改缓冲区); |
核心规则
- 生产者流程:P (empty) → P (mutex) → 放入产品 → V (mutex) → V (full);
- 消费者流程 :P (full) → P (mutex) → 取出产品 → V (mutex) → V (empty);⚠️ 注意:同步信号量(empty/full)必须在互斥信号量(mutex)之前执行 P 操作,否则会导致死锁。
Python代码
python
import threading
import time
import random
from collections import deque
from semaphore import Semaphore
# ===================== 全局配置 =====================
BUFFER_SIZE = 5 # 缓冲区大小
buffer = deque() # 缓冲区(队列实现,FIFO)
# 初始化信号量
empty = Semaphore(BUFFER_SIZE) # 空缓冲区数量,初始为5
full = Semaphore(0) # 满缓冲区数量,初始为0
mutex = Semaphore(1) # 互斥信号量,保护缓冲区
# 生产/消费计数(用于验证最终结果)
produce_count = 0
consume_count = 0
count_lock = threading.Lock() # 保护计数的互斥锁
# ===================== 生产者/消费者逻辑 =====================
def producer(prod_id):
"""生产者进程:生产产品并放入缓冲区"""
global produce_count
# 每个生产者生产10个产品(可调整)
for i in range(10):
# 步骤1:生产产品(非临界区)
product = f"产品-{prod_id}-{i}"
time.sleep(random.uniform(0.1, 0.3)) # 模拟生产耗时
print(f"📦 生产者{prod_id}生产了 {product}")
# 步骤2:申请空缓冲区(同步约束:缓冲区满则阻塞)
empty.P()
# 步骤3:申请缓冲区互斥锁(互斥约束:独占缓冲区)
mutex.P()
# 步骤4:临界区操作------放入缓冲区
buffer.append(product)
print(f"🚚 生产者{prod_id}放入 {product} | 缓冲区:{list(buffer)}")
# 步骤5:释放互斥锁
mutex.V()
# 步骤6:释放满缓冲区(通知消费者:有产品可消费)
full.V()
# 更新生产计数
with count_lock:
produce_count += 1
def consumer(cons_id):
"""消费者进程:从缓冲区取出产品并消费"""
global consume_count
# 每个消费者消费10个产品(与生产者数量匹配)
for i in range(10):
# 步骤1:申请满缓冲区(同步约束:缓冲区空则阻塞)
full.P()
# 步骤2:申请缓冲区互斥锁
mutex.P()
# 步骤3:临界区操作------取出产品
product = buffer.popleft()
print(f"🛒 消费者{cons_id}取出 {product} | 缓冲区:{list(buffer)}")
# 步骤4:释放互斥锁
mutex.V()
# 步骤5:释放空缓冲区(通知生产者:有位置可生产)
empty.V()
# 步骤6:消费产品(非临界区)
time.sleep(random.uniform(0.2, 0.4)) # 模拟消费耗时
print(f"🍽️ 消费者{cons_id}消费了 {product}")
# 更新消费计数
with count_lock:
consume_count += 1
# ===================== 测试代码 =====================
def test_producer_consumer():
# 创建2个生产者、2个消费者(可调整数量)
producers = [threading.Thread(target=producer, args=(i,)) for i in range(2)]
consumers = [threading.Thread(target=consumer, args=(i,)) for i in range(2)]
# 启动所有线程
print("===== 生产者-消费者问题测试 =====")
for p in producers:
p.start()
for c in consumers:
c.start()
# 等待所有线程结束
for p in producers:
p.join()
for c in consumers:
c.join()
# 验证结果
print("\n===== 最终统计 =====")
print(f"生产者总计生产:{produce_count} 个产品")
print(f"消费者总计消费:{consume_count} 个产品")
print(f"缓冲区剩余产品:{len(buffer)} 个")
# 执行测试
if __name__ == "__main__":
test_producer_consumer()
程序运行结果展示
bash
===== 生产者-消费者问题测试 =====
📦 生产者0生产了 产品-0-0
🚚 生产者0放入 产品-0-0 | 缓冲区:['产品-0-0']
🛒 消费者0取出 产品-0-0 | 缓冲区:[]
📦 生产者1生产了 产品-1-0
🚚 生产者1放入 产品-1-0 | 缓冲区:['产品-1-0']
🛒 消费者1取出 产品-1-0 | 缓冲区:[]
📦 生产者0生产了 产品-0-1
🚚 生产者0放入 产品-0-1 | 缓冲区:['产品-0-1']
📦 生产者0生产了 产品-0-2
🚚 生产者0放入 产品-0-2 | 缓冲区:['产品-0-1', '产品-0-2']
🍽️ 消费者0消费了 产品-0-0
🛒 消费者0取出 产品-0-1 | 缓冲区:['产品-0-2']
🍽️ 消费者1消费了 产品-1-0
🛒 消费者1取出 产品-0-2 | 缓冲区:[]
📦 生产者1生产了 产品-1-1
🚚 生产者1放入 产品-1-1 | 缓冲区:['产品-1-1']
📦 生产者0生产了 产品-0-3
🚚 生产者0放入 产品-0-3 | 缓冲区:['产品-1-1', '产品-0-3']
📦 生产者1生产了 产品-1-2
🚚 生产者1放入 产品-1-2 | 缓冲区:['产品-1-1', '产品-0-3', '产品-1-2']
🍽️ 消费者0消费了 产品-0-1
🛒 消费者0取出 产品-1-1 | 缓冲区:['产品-0-3', '产品-1-2']
🍽️ 消费者1消费了 产品-0-2
🛒 消费者1取出 产品-0-3 | 缓冲区:['产品-1-2']
📦 生产者0生产了 产品-0-4
🚚 生产者0放入 产品-0-4 | 缓冲区:['产品-1-2', '产品-0-4']
🍽️ 消费者0消费了 产品-1-1
🛒 消费者0取出 产品-1-2 | 缓冲区:['产品-0-4']
📦 生产者1生产了 产品-1-3
🚚 生产者1放入 产品-1-3 | 缓冲区:['产品-0-4', '产品-1-3']
📦 生产者0生产了 产品-0-5
🚚 生产者0放入 产品-0-5 | 缓冲区:['产品-0-4', '产品-1-3', '产品-0-5']
🍽️ 消费者0消费了 产品-1-2
🛒 消费者0取出 产品-0-4 | 缓冲区:['产品-1-3', '产品-0-5']
🍽️ 消费者1消费了 产品-0-3
🛒 消费者1取出 产品-1-3 | 缓冲区:['产品-0-5']
📦 生产者1生产了 产品-1-4
🚚 生产者1放入 产品-1-4 | 缓冲区:['产品-0-5', '产品-1-4']
📦 生产者0生产了 产品-0-6
🚚 生产者0放入 产品-0-6 | 缓冲区:['产品-0-5', '产品-1-4', '产品-0-6']
📦 生产者1生产了 产品-1-5
🚚 生产者1放入 产品-1-5 | 缓冲区:['产品-0-5', '产品-1-4', '产品-0-6', '产品-1-5']
🍽️ 消费者0消费了 产品-0-4
🛒 消费者0取出 产品-0-5 | 缓冲区:['产品-1-4', '产品-0-6', '产品-1-5']
📦 生产者1生产了 产品-1-6
🚚 生产者1放入 产品-1-6 | 缓冲区:['产品-1-4', '产品-0-6', '产品-1-5', '产品-1-6']
🍽️ 消费者1消费了 产品-1-3
🛒 消费者1取出 产品-1-4 | 缓冲区:['产品-0-6', '产品-1-5', '产品-1-6']
📦 生产者0生产了 产品-0-7
🚚 生产者0放入 产品-0-7 | 缓冲区:['产品-0-6', '产品-1-5', '产品-1-6', '产品-0-7']
📦 生产者1生产了 产品-1-7
🚚 生产者1放入 产品-1-7 | 缓冲区:['产品-0-6', '产品-1-5', '产品-1-6', '产品-0-7', '产品-1-7']
🍽️ 消费者1消费了 产品-1-4
🛒 消费者1取出 产品-0-6 | 缓冲区:['产品-1-5', '产品-1-6', '产品-0-7', '产品-1-7']
🍽️ 消费者0消费了 产品-0-5
🛒 消费者0取出 产品-1-5 | 缓冲区:['产品-1-6', '产品-0-7', '产品-1-7']
📦 生产者0生产了 产品-0-8
🚚 生产者0放入 产品-0-8 | 缓冲区:['产品-1-6', '产品-0-7', '产品-1-7', '产品-0-8']
📦 生产者1生产了 产品-1-8
🚚 生产者1放入 产品-1-8 | 缓冲区:['产品-1-6', '产品-0-7', '产品-1-7', '产品-0-8', '产品-1-8']
🍽️ 消费者1消费了 产品-0-6
🛒 消费者1取出 产品-1-6 | 缓冲区:['产品-0-7', '产品-1-7', '产品-0-8', '产品-1-8']
📦 生产者0生产了 产品-0-9
🚚 生产者0放入 产品-0-9 | 缓冲区:['产品-0-7', '产品-1-7', '产品-0-8', '产品-1-8', '产品-0-9']
🍽️ 消费者0消费了 产品-1-5
🛒 消费者0取出 产品-0-7 | 缓冲区:['产品-1-7', '产品-0-8', '产品-1-8', '产品-0-9']
📦 生产者1生产了 产品-1-9
🚚 生产者1放入 产品-1-9 | 缓冲区:['产品-1-7', '产品-0-8', '产品-1-8', '产品-0-9', '产品-1-9']
🍽️ 消费者1消费了 产品-1-6
🛒 消费者1取出 产品-1-7 | 缓冲区:['产品-0-8', '产品-1-8', '产品-0-9', '产品-1-9']
🍽️ 消费者0消费了 产品-0-7
🛒 消费者0取出 产品-0-8 | 缓冲区:['产品-1-8', '产品-0-9', '产品-1-9']
🍽️ 消费者1消费了 产品-1-7
🛒 消费者1取出 产品-1-8 | 缓冲区:['产品-0-9', '产品-1-9']
🍽️ 消费者0消费了 产品-0-8
🛒 消费者0取出 产品-0-9 | 缓冲区:['产品-1-9']
🍽️ 消费者1消费了 产品-1-8
🛒 消费者1取出 产品-1-9 | 缓冲区:[]
🍽️ 消费者0消费了 产品-0-9
🍽️ 消费者1消费了 产品-1-9
===== 最终统计 =====
生产者总计生产:20 个产品
消费者总计消费:20 个产品
缓冲区剩余产品:0 个
读者 - 写者问题
问题背景
多个读者可以同时读取共享资源,但写者需要独占资源;读者和写者互斥,写者和写者互斥。读者优先 是指:只要有读者在读取,写者就必须等待;即使写者先申请,后续到达的读者仍会优先获得资源。
核心思路
- 用
read_count记录当前正在读取的读者数量; mutex信号量(初始值 1):保护read_count的互斥访问(读者进入 / 退出时修改该变量);rw_mutex信号量(初始值 1):实现读者 / 写者、写者 / 写者的互斥(只有第一个读者会 P 操作,最后一个读者会 V 操作);- 写者直接操作
rw_mutex,保证独占资源。
Python代码
python
import threading
import time
from semaphore import Semaphore
# 全局共享资源
shared_data = "初始数据"
# 读者计数(需要互斥访问)
read_count = 0
# 信号量初始化
mutex = Semaphore(1) # 保护read_count的互斥信号量
rw_mutex = Semaphore(1) # 读写互斥信号量
def reader(reader_id):
"""读者进程"""
global read_count, shared_data
for _ in range(2): # 每个读者读取2次
# 1. 进入临界区,修改read_count
mutex.P()
read_count += 1
# 第一个读者需要申请读写互斥信号量,阻止写者
if read_count == 1:
rw_mutex.P()
mutex.V() # 释放read_count的互斥锁
# 2. 读取共享资源(读者可并发)
print(f"📖 读者{reader_id}开始读取,当前数据:{shared_data}")
time.sleep(0.2) # 模拟读取耗时
print(f"📖 读者{reader_id}读取完成")
# 3. 退出临界区,修改read_count
mutex.P()
read_count -= 1
# 最后一个读者释放读写互斥信号量,允许写者
if read_count == 0:
rw_mutex.V()
mutex.V()
# 读取间隔
time.sleep(0.1)
def writer(writer_id):
"""写者进程"""
global shared_data
for i in range(2): # 每个写者写入2次
# 1. 申请读写互斥信号量(独占资源)
rw_mutex.P()
# 2. 写入共享资源(写者独占)
new_data = f"写者{writer_id}-数据{i}"
print(f"✍️ 写者{writer_id}开始写入,原数据:{shared_data}")
time.sleep(0.3) # 模拟写入耗时
shared_data = new_data
print(f"✍️ 写者{writer_id}写入完成,新数据:{shared_data}")
# 3. 释放读写互斥信号量
rw_mutex.V()
# 写入间隔
time.sleep(0.2)
# 测试读者-写者问题
if __name__ == "__main__":
# 创建3个读者,2个写者
readers = [threading.Thread(target=reader, args=(i,)) for i in range(3)]
writers = [threading.Thread(target=writer, args=(i,)) for i in range(2)]
# 启动所有线程(读者先启动,体现读者优先)
for r in readers:
r.start()
time.sleep(0.1) # 让读者先抢占资源
for w in writers:
w.start()
# 等待所有线程结束
for r in readers:
r.join()
for w in writers:
w.join()
print("\n✅ 读写操作全部完成,最终数据:", shared_data)
程序运行结果展示
bash
📖 读者0开始读取,当前数据:初始数据
📖 读者1开始读取,当前数据:初始数据
📖 读者2开始读取,当前数据:初始数据
📖 读者0读取完成
📖 读者1读取完成📖 读者2读取完成
✍️ 写者0开始写入,原数据:初始数据
✍️ 写者0写入完成,新数据:写者0-数据0
✍️ 写者1开始写入,原数据:写者0-数据0
✍️ 写者1写入完成,新数据:写者1-数据0
📖 读者0开始读取,当前数据:写者1-数据0📖 读者1开始读取,当前数据:写者1-数据0
📖 读者2开始读取,当前数据:写者1-数据0
📖 读者2读取完成
📖 读者0读取完成
📖 读者1读取完成
✍️ 写者0开始写入,原数据:写者1-数据0
✍️ 写者0写入完成,新数据:写者0-数据1
✍️ 写者1开始写入,原数据:写者0-数据1
✍️ 写者1写入完成,新数据:写者1-数据1
✅ 读写操作全部完成,最终数据: 写者1-数据1
哲学家进餐问题
问题背景
5 个哲学家围坐在圆桌旁,每人左右各有一根筷子,思考 / 进餐交替进行:
- 进餐需要同时拿起左右两根筷子;
- 思考时放下筷子;
- 若所有哲学家同时拿起左手筷子,会导致死锁(都等待右手筷子)。
核心思路(解决死锁)
采用「最多允许 4 个哲学家同时拿筷子」的策略:
- 用
chopsticks列表表示 5 根筷子(每个元素是信号量,初始值 1); - 用
eating_allowance信号量(初始值 4):限制同时拿筷子的哲学家数量(最多 4 个),避免 5 人同时拿左手筷子导致死锁; - 哲学家拿筷子顺序:先申请「允许进餐」→ 拿左手筷子 → 拿右手筷子 → 进餐 → 放下筷子 → 释放「允许进餐」。
Python代码
python
import threading
import time
from semaphore import Semaphore
# 初始化5根筷子(信号量,初始值1)
chopsticks = [Semaphore(1) for _ in range(5)]
# 最多允许4个哲学家同时拿筷子(解决死锁)
eating_allowance = Semaphore(4)
def philosopher(philosopher_id):
"""哲学家进程(思考-进餐循环)"""
# 左右手筷子的索引(环形)
left_chopstick = philosopher_id
right_chopstick = (philosopher_id + 1) % 5
for _ in range(3): # 每个哲学家循环3次(思考+进餐)
# 1. 思考阶段
print(f"🤔 哲学家{philosopher_id}开始思考")
time.sleep(0.5) # 模拟思考耗时
# 2. 申请进餐权限(最多4人同时申请筷子)
eating_allowance.P()
# 3. 拿左手筷子
chopsticks[left_chopstick].P()
print(f"🍜 哲学家{philosopher_id}拿起左手筷子{left_chopstick}")
# 4. 拿右手筷子
chopsticks[right_chopstick].P()
print(f"🍜 哲学家{philosopher_id}拿起右手筷子{right_chopstick}")
# 5. 进餐阶段
print(f"🍚 哲学家{philosopher_id}开始进餐")
time.sleep(0.8) # 模拟进餐耗时
print(f"🍚 哲学家{philosopher_id}进餐完成")
# 6. 放下右手筷子
chopsticks[right_chopstick].V()
print(f"🥢 哲学家{philosopher_id}放下右手筷子{right_chopstick}")
# 7. 放下左手筷子
chopsticks[left_chopstick].V()
print(f"🥢 哲学家{philosopher_id}放下左手筷子{left_chopstick}")
# 8. 释放进餐权限
eating_allowance.V()
# 测试哲学家进餐问题
if __name__ == "__main__":
# 创建5个哲学家线程
philosophers = [threading.Thread(target=philosopher, args=(i,)) for i in range(5)]
# 启动所有线程
for p in philosophers:
p.start()
# 等待所有线程结束
for p in philosophers:
p.join()
print("\n✅ 所有哲学家完成进餐循环")
程序运行结果展示
bash
🤔 哲学家0开始思考
🤔 哲学家1开始思考
🤔 哲学家2开始思考
🤔 哲学家3开始思考
🤔 哲学家4开始思考
🍜 哲学家1拿起左手筷子1🍜 哲学家0拿起左手筷子0
🍜 哲学家1拿起右手筷子2
🍚 哲学家1开始进餐
🍜 哲学家4拿起左手筷子4
🍚 哲学家1进餐完成
🥢 哲学家1放下右手筷子2
🍜 哲学家2拿起左手筷子2🥢 哲学家1放下左手筷子1🍜 哲学家0拿起右手筷子1
🤔 哲学家1开始思考🍜 哲学家3拿起左手筷子3
🍚 哲学家0开始进餐
🍚 哲学家0进餐完成
🥢 哲学家0放下右手筷子1
🥢 哲学家0放下左手筷子0
🤔 哲学家0开始思考
🍜 哲学家1拿起左手筷子1🍜 哲学家4拿起右手筷子0
🍚 哲学家4开始进餐
🍚 哲学家4进餐完成
🥢 哲学家4放下右手筷子0
🥢 哲学家4放下左手筷子4
🤔 哲学家4开始思考
🍜 哲学家0拿起左手筷子0🍜 哲学家3拿起右手筷子4
🍚 哲学家3开始进餐
🍚 哲学家3进餐完成
🥢 哲学家3放下右手筷子4
🥢 哲学家3放下左手筷子3
🤔 哲学家3开始思考
🍜 哲学家4拿起左手筷子4🍜 哲学家2拿起右手筷子3
🍚 哲学家2开始进餐
🍚 哲学家2进餐完成
🥢 哲学家2放下右手筷子3
🥢 哲学家2放下左手筷子2
🤔 哲学家2开始思考
🍜 哲学家1拿起右手筷子2
🍚 哲学家1开始进餐
🍜 哲学家3拿起左手筷子3
🍚 哲学家1进餐完成
🥢 哲学家1放下右手筷子2
🥢 哲学家1放下左手筷子1
🤔 哲学家1开始思考
🍜 哲学家2拿起左手筷子2
🍜 哲学家0拿起右手筷子1
🍚 哲学家0开始进餐
🍚 哲学家0进餐完成
🥢 哲学家0放下右手筷子1
🥢 哲学家0放下左手筷子0
🤔 哲学家0开始思考🍜 哲学家4拿起右手筷子0
🍚 哲学家4开始进餐
🍜 哲学家1拿起左手筷子1
🍚 哲学家4进餐完成
🥢 哲学家4放下右手筷子0
🥢 哲学家4放下左手筷子4
🤔 哲学家4开始思考
🍜 哲学家0拿起左手筷子0
🍜 哲学家3拿起右手筷子4
🍚 哲学家3开始进餐
🍚 哲学家3进餐完成
🥢 哲学家3放下右手筷子4
🥢 哲学家3放下左手筷子3
🤔 哲学家3开始思考
🍜 哲学家4拿起左手筷子4
🍜 哲学家2拿起右手筷子3
🍚 哲学家2开始进餐
🍚 哲学家2进餐完成
🥢 哲学家2放下右手筷子3
🥢 哲学家2放下左手筷子2
🤔 哲学家2开始思考
🍜 哲学家3拿起左手筷子3🍜 哲学家1拿起右手筷子2
🍚 哲学家1开始进餐
🍚 哲学家1进餐完成
🥢 哲学家1放下右手筷子2
🥢 哲学家1放下左手筷子1
🍜 哲学家0拿起右手筷子1
🍚 哲学家0开始进餐
🍜 哲学家2拿起左手筷子2
🍚 哲学家0进餐完成
🥢 哲学家0放下右手筷子1
🥢 哲学家0放下左手筷子0
🍜 哲学家4拿起右手筷子0
🍚 哲学家4开始进餐
🍚 哲学家4进餐完成
🥢 哲学家4放下右手筷子0
🥢 哲学家4放下左手筷子4🍜 哲学家3拿起右手筷子4
🍚 哲学家3开始进餐
🍚 哲学家3进餐完成
🥢 哲学家3放下右手筷子4
🥢 哲学家3放下左手筷子3
🍜 哲学家2拿起右手筷子3
🍚 哲学家2开始进餐
🍚 哲学家2进餐完成
🥢 哲学家2放下右手筷子3
🥢 哲学家2放下左手筷子2
✅ 所有哲学家完成进餐循环
3. 管程(Monitor)
- 原理:将共享资源和对共享资源的操作封装在一个模块中,进程只能通过管程提供的接口访问共享资源,管程内部保证同一时刻只有一个进程执行,从而实现互斥。
- 特点 :比信号量更易于理解和使用,避免了 PV 操作的滥用导致的死锁;Java 中的
synchronized关键字就是管程机制的实现。
Python代码
python
import threading
import time
class Monitor:
"""模拟管程(Monitor)"""
def __init__(self):
self.lock = threading.Lock() # 管程互斥锁
self.condition = threading.Condition(self.lock) # 条件变量
self.shared_resource = 0 # 共享资源
def increment(self, process_id):
"""管程提供的接口:增加共享资源"""
with self.lock: # 管程保证同一时刻只有一个进程执行
print(f"进程{process_id}进入管程,当前资源:{self.shared_resource}")
self.shared_resource += 1
print(f"进程{process_id}修改资源为:{self.shared_resource}")
# 模拟条件等待(可选)
self.condition.wait(0.1)
print(f"进程{process_id}退出管程")
def decrement(self, process_id):
"""管程提供的接口:减少共享资源"""
with self.lock:
print(f"进程{process_id}进入管程,当前资源:{self.shared_resource}")
self.shared_resource -= 1
print(f"进程{process_id}修改资源为:{self.shared_resource}")
self.condition.wait(0.1)
print(f"进程{process_id}退出管程")
# 测试管程
def worker(monitor, pid, op_type):
"""工作进程"""
for _ in range(2):
if op_type == "inc":
monitor.increment(pid)
else:
monitor.decrement(pid)
time.sleep(0.1)
if __name__ == "__main__":
monitor = Monitor()
# 创建4个进程:2个加,2个减
threads = [
threading.Thread(target=worker, args=(monitor, 1, "inc")),
threading.Thread(target=worker, args=(monitor, 2, "inc")),
threading.Thread(target=worker, args=(monitor, 3, "dec")),
threading.Thread(target=worker, args=(monitor, 4, "dec"))
]
for t in threads:
t.start()
for t in threads:
t.join()
print(f"最终共享资源值:{monitor.shared_resource}")
程序运行结果展示
bash
进程1进入管程,当前资源:0
进程1修改资源为:1
进程2进入管程,当前资源:1
进程2修改资源为:2
进程3进入管程,当前资源:2
进程3修改资源为:1
进程4进入管程,当前资源:1
进程4修改资源为:0
进程3退出管程
进程1退出管程
进程4退出管程
进程2退出管程
进程4进入管程,当前资源:0
进程4修改资源为:-1
进程3进入管程,当前资源:-1
进程3修改资源为:-2
进程1进入管程,当前资源:-2
进程1修改资源为:-1
进程2进入管程,当前资源:-1
进程2修改资源为:0
进程4退出管程
进程1退出管程
进程2退出管程
进程3退出管程
最终共享资源值:0
四、 死锁处理算法
死锁是指多个进程因竞争资源 或进程推进顺序不当 ,导致彼此无限等待的状态,死锁的产生需要满足互斥、请求与保持、不剥夺、循环等待四个必要条件。
1. 死锁预防算法
- 原理 :破坏死锁的四个必要条件之一 ,从根源上防止死锁发生。
- 破坏 "请求与保持":进程一次性申请所有需要的资源,运行过程中不再申请新资源。
- 破坏 "不剥夺":进程申请新资源失败时,主动释放已占有的资源。
- 破坏 "循环等待":对资源进行编号,进程必须按升序顺序申请资源。
- 特点:实现简单,但资源利用率低,系统吞吐量下降。
算法 1:破坏 "请求与保持"(一次性申请所有资源)
核心逻辑
进程启动时一次性申请所有需要的资源:
- 若所有资源都可用,一次性分配给进程,进程运行至结束后释放所有资源;
- 若有任意一个资源不可用,不分配任何资源,进程等待,直到所有资源都能被分配;
- 进程运行过程中不允许申请新资源,彻底破坏 "请求与保持" 条件。
Python代码
python
import time
import random
# 资源类:表示系统中的一个资源(如打印机、CPU、内存等)
class Resource:
def __init__(self, res_id, res_name):
self.id = res_id # 资源编号(用于循环等待算法的升序检查)
self.name = res_name # 资源名称
self.is_available = True # 资源是否可用
self.occupied_by = None # 被哪个进程占用(进程ID)
def __repr__(self):
return f"Resource({self.id}, {self.name}, 可用={self.is_available}, 被进程{self.occupied_by}占用)"
# 资源管理器:管理系统中所有资源的分配与释放
class ResourceManager:
def __init__(self, resources):
self.resources = {res.id: res for res in resources} # 按ID索引资源
self.lock = False # 模拟原子操作的锁(避免多进程同时修改资源状态)
# 检查资源是否可用(加锁保证原子性)
def _check_lock(self):
while self.lock:
time.sleep(0.001)
self.lock = True
def _release_lock(self):
self.lock = False
# 获取资源的当前状态(辅助打印)
def get_resource_status(self):
return "\n".join([str(res) for res in self.resources.values()])
# 破坏"请求与保持"的进程类
class ProcessNoRequestHold:
def __init__(self, pid, required_res_ids):
self.pid = pid # 进程ID
self.required_res_ids = required_res_ids # 进程需要的所有资源ID(一次性申请)
self.status = "WAITING" # 状态:WAITING/RUNNING/FINISHED
# 一次性申请所有资源
def request_all_resources(self, manager):
manager._check_lock() # 加锁保证原子性
try:
# 检查所有需要的资源是否都可用
all_available = True
for res_id in self.required_res_ids:
res = manager.resources[res_id]
if not res.is_available:
all_available = False
break
# 所有资源可用:一次性分配
if all_available:
for res_id in self.required_res_ids:
res = manager.resources[res_id]
res.is_available = False
res.occupied_by = self.pid
self.status = "RUNNING"
print(f"🔵 进程{self.pid}:一次性申请到所有资源,开始运行(资源ID:{self.required_res_ids})")
return True
# 有资源不可用:不分配任何资源,继续等待
else:
print(f"🟡 进程{self.pid}:部分资源被占用,无法一次性申请,继续等待")
return False
finally:
manager._release_lock()
# 运行进程(运行完成后释放所有资源)
def run(self, manager):
if self.status != "RUNNING":
return
print(f"🟢 进程{self.pid}:开始执行任务(占用资源:{self.required_res_ids})")
time.sleep(random.uniform(0.5, 1.0)) # 模拟进程运行耗时
# 释放所有资源
manager._check_lock()
try:
for res_id in self.required_res_ids:
res = manager.resources[res_id]
res.is_available = True
res.occupied_by = None
self.status = "FINISHED"
print(f"🟣 进程{self.pid}:任务完成,释放所有资源(资源ID:{self.required_res_ids})")
finally:
manager._release_lock()
# 测试:破坏"请求与保持"
def test_no_request_hold():
# 1. 初始化系统资源(资源ID:0=打印机,1=CPU,2=内存,3=磁盘)
resources = [
Resource(0, "打印机"),
Resource(1, "CPU"),
Resource(2, "内存"),
Resource(3, "磁盘")
]
manager = ResourceManager(resources)
# 2. 定义进程(进程0需要CPU+内存,进程1需要打印机+磁盘,进程2需要CPU+打印机)
process0 = ProcessNoRequestHold(0, [1, 2])
process1 = ProcessNoRequestHold(1, [0, 3])
process2 = ProcessNoRequestHold(2, [1, 0])
processes = [process0, process1, process2]
# 3. 模拟资源分配与进程运行
print("===== 测试:破坏请求与保持(一次性申请所有资源) =====")
# 先让进程0和1运行(资源不冲突)
process0.request_all_resources(manager)
process0.run(manager)
process1.request_all_resources(manager)
process1.run(manager)
# 进程2申请CPU+打印机(此时CPU被进程0占用,打印机空闲,无法一次性申请)
process2.request_all_resources(manager)
print("\n当前资源状态:")
print(manager.get_resource_status())
# 进程0完成后,进程2重新申请(此时CPU释放,可一次性申请)
time.sleep(1)
print("\n进程0完成后,进程2重新申请:")
process2.request_all_resources(manager)
process2.run(manager)
print("\n最终资源状态:")
print(manager.get_resource_status())
# 执行测试
test_no_request_hold()
程序运行结果展示
bash
===== 测试:破坏请求与保持(一次性申请所有资源) =====
🔵 进程0:一次性申请到所有资源,开始运行(资源ID:[1, 2])
🟢 进程0:开始执行任务(占用资源:[1, 2])
🟣 进程0:任务完成,释放所有资源(资源ID:[1, 2])
🔵 进程1:一次性申请到所有资源,开始运行(资源ID:[0, 3])
🟢 进程1:开始执行任务(占用资源:[0, 3])
🟣 进程1:任务完成,释放所有资源(资源ID:[0, 3])
🔵 进程2:一次性申请到所有资源,开始运行(资源ID:[1, 0])
当前资源状态:
Resource(0, 打印机, 可用=False, 被进程2占用)
Resource(1, CPU, 可用=False, 被进程2占用)
Resource(2, 内存, 可用=True, 被进程None占用)
Resource(3, 磁盘, 可用=True, 被进程None占用)
进程0完成后,进程2重新申请:
🟡 进程2:部分资源被占用,无法一次性申请,继续等待
🟢 进程2:开始执行任务(占用资源:[1, 0])
🟣 进程2:任务完成,释放所有资源(资源ID:[1, 0])
最终资源状态:
Resource(0, 打印机, 可用=True, 被进程None占用)
Resource(1, CPU, 可用=True, 被进程None占用)
Resource(2, 内存, 可用=True, 被进程None占用)
Resource(3, 磁盘, 可用=True, 被进程None占用)
算法 2:破坏 "不剥夺"(申请失败则释放已占资源)
核心逻辑
进程运行中可逐步申请资源,但申请新资源失败时,主动释放已占有的所有资源,进入等待状态;之后重新申请所有需要的资源(包括已释放的),彻底破坏 "不剥夺" 条件(资源可被主动剥夺)。
Python代码
python
import time
import random
# 资源类:表示系统中的一个资源(如打印机、CPU、内存等)
class Resource:
def __init__(self, res_id, res_name):
self.id = res_id # 资源编号(用于循环等待算法的升序检查)
self.name = res_name # 资源名称
self.is_available = True # 资源是否可用
self.occupied_by = None # 被哪个进程占用(进程ID)
def __repr__(self):
return f"Resource({self.id}, {self.name}, 可用={self.is_available}, 被进程{self.occupied_by}占用)"
# 资源管理器:管理系统中所有资源的分配与释放
class ResourceManager:
def __init__(self, resources):
self.resources = {res.id: res for res in resources} # 按ID索引资源
self.lock = False # 模拟原子操作的锁(避免多进程同时修改资源状态)
# 检查资源是否可用(加锁保证原子性)
def _check_lock(self):
while self.lock:
time.sleep(0.001)
self.lock = True
def _release_lock(self):
self.lock = False
# 获取资源的当前状态(辅助打印)
def get_resource_status(self):
return "\n".join([str(res) for res in self.resources.values()])
# 破坏"不剥夺"的进程类
class ProcessNoPreemption:
def __init__(self, pid, required_res_ids):
self.pid = pid # 进程ID
self.required_res_ids = required_res_ids # 需要的所有资源(分步申请)
self.owned_res_ids = [] # 已占用的资源ID
self.status = "WAITING" # 状态:WAITING/RUNNING/FINISHED
self.request_index = 0 # 当前申请到第几个资源
# 分步申请资源(失败则释放已占资源)
def request_resource(self, manager):
if self.status == "FINISHED":
return False
manager._check_lock()
try:
# 若已申请完所有资源,直接运行
if self.request_index >= len(self.required_res_ids):
self.status = "RUNNING"
return True
# 申请下一个资源
current_res_id = self.required_res_ids[self.request_index]
res = manager.resources[current_res_id]
# 资源可用:分配
if res.is_available:
res.is_available = False
res.occupied_by = self.pid
self.owned_res_ids.append(current_res_id)
self.request_index += 1
print(f"🔵 进程{self.pid}:成功申请资源{current_res_id}({res.name}),已占用:{self.owned_res_ids}")
return True
# 资源不可用:剥夺已占资源,重置申请状态
else:
print(f"🟡 进程{self.pid}:申请资源{current_res_id}失败,释放已占资源{self.owned_res_ids}")
# 释放所有已占用的资源
for res_id in self.owned_res_ids:
r = manager.resources[res_id]
r.is_available = True
r.occupied_by = None
# 重置状态
self.owned_res_ids = []
self.request_index = 0
self.status = "WAITING"
return False
finally:
manager._release_lock()
# 运行进程(完成后释放所有资源)
def run(self, manager):
if self.request_index < len(self.required_res_ids):
print(f"🟠 进程{self.pid}:未申请完所有资源,无法运行")
return
self.status = "RUNNING"
print(f"🟢 进程{self.pid}:开始执行任务(占用资源:{self.owned_res_ids})")
time.sleep(random.uniform(0.5, 1.0))
# 释放所有资源
manager._check_lock()
try:
for res_id in self.owned_res_ids:
res = manager.resources[res_id]
res.is_available = True
res.occupied_by = None
self.status = "FINISHED"
self.owned_res_ids = []
print(f"🟣 进程{self.pid}:任务完成,释放所有资源")
finally:
manager._release_lock()
# 测试:破坏"不剥夺"
def test_no_preemption():
# 1. 初始化资源
resources = [
Resource(0, "打印机"),
Resource(1, "CPU"),
Resource(2, "内存")
]
manager = ResourceManager(resources)
# 2. 定义进程(进程0需要CPU→内存,进程1需要内存→CPU)
process0 = ProcessNoPreemption(0, [1, 2])
process1 = ProcessNoPreemption(1, [2, 1])
processes = [process0, process1]
# 3. 模拟资源申请(触发"不剥夺"逻辑)
print("===== 测试:破坏不剥夺(申请失败则释放已占资源) =====")
# 进程0先申请CPU(成功)
process0.request_resource(manager)
# 进程1申请内存(成功)
process1.request_resource(manager)
# 进程0申请内存(被进程1占用,申请失败→释放CPU)
process0.request_resource(manager)
# 进程1申请CPU(此时CPU已被进程0释放,申请成功)
process1.request_resource(manager)
print("\n当前资源状态:")
print(manager.get_resource_status())
# 进程1运行并释放资源
process1.run(manager)
# 进程0重新申请所有资源
process0.request_resource(manager)
process0.request_resource(manager)
process0.run(manager)
print("\n最终资源状态:")
print(manager.get_resource_status())
# 执行测试
test_no_preemption()
程序运行结果展示
bash
===== 测试:破坏不剥夺(申请失败则释放已占资源) =====
🔵 进程0:成功申请资源1(CPU),已占用:[1]
🔵 进程1:成功申请资源2(内存),已占用:[2]
🟡 进程0:申请资源2失败,释放已占资源[1]
🔵 进程1:成功申请资源1(CPU),已占用:[2, 1]
当前资源状态:
Resource(0, 打印机, 可用=True, 被进程None占用)
Resource(1, CPU, 可用=False, 被进程1占用)
Resource(2, 内存, 可用=False, 被进程1占用)
🟢 进程1:开始执行任务(占用资源:[2, 1])
🟣 进程1:任务完成,释放所有资源
🔵 进程0:成功申请资源1(CPU),已占用:[1]
🔵 进程0:成功申请资源2(内存),已占用:[1, 2]
🟢 进程0:开始执行任务(占用资源:[1, 2])
🟣 进程0:任务完成,释放所有资源
最终资源状态:
Resource(0, 打印机, 可用=True, 被进程None占用)
Resource(1, CPU, 可用=True, 被进程None占用)
Resource(2, 内存, 可用=True, 被进程None占用)
算法 3:破坏 "循环等待"(资源升序申请)
核心逻辑
给所有资源分配唯一编号,进程必须按资源编号升序申请(不能跳号、不能逆序),管理器仅接受升序申请;这样系统中不会形成 "进程 A 等进程 B,进程 B 等进程 A" 的循环等待链,彻底破坏 "循环等待" 条件。
Python代码
python
import time
import random
# 资源类:表示系统中的一个资源(如打印机、CPU、内存等)
class Resource:
def __init__(self, res_id, res_name):
self.id = res_id # 资源编号(用于循环等待算法的升序检查)
self.name = res_name # 资源名称
self.is_available = True # 资源是否可用
self.occupied_by = None # 被哪个进程占用(进程ID)
def __repr__(self):
return f"Resource({self.id}, {self.name}, 可用={self.is_available}, 被进程{self.occupied_by}占用)"
# 资源管理器:管理系统中所有资源的分配与释放
class ResourceManager:
def __init__(self, resources):
self.resources = {res.id: res for res in resources} # 按ID索引资源
self.lock = False # 模拟原子操作的锁(避免多进程同时修改资源状态)
# 检查资源是否可用(加锁保证原子性)
def _check_lock(self):
while self.lock:
time.sleep(0.001)
self.lock = True
def _release_lock(self):
self.lock = False
# 获取资源的当前状态(辅助打印)
def get_resource_status(self):
return "\n".join([str(res) for res in self.resources.values()])
# 破坏"循环等待"的资源管理器(仅接受升序申请)
class AscendingResourceManager(ResourceManager):
# 检查申请是否符合升序规则
def check_ascending(self, current_res_id, last_res_id):
# 首次申请无限制,非首次必须大于上一次申请的资源ID
return last_res_id is None or current_res_id > last_res_id
# 升序申请资源
def request_resource_ascending(self, pid, res_id, last_res_id):
self._check_lock()
try:
res = self.resources[res_id]
# 第一步:检查是否升序
if not self.check_ascending(res_id, last_res_id):
print(f"🟡 进程{pid}:申请资源{res_id}(逆序,上一次申请{last_res_id}),拒绝申请")
return False
# 第二步:检查资源是否可用
if not res.is_available:
print(f"🟡 进程{pid}:申请资源{res_id}(被进程{res.occupied_by}占用),等待")
return False
# 第三步:分配资源
res.is_available = False
res.occupied_by = pid
print(f"🔵 进程{pid}:成功申请资源{res_id}({res.name}),符合升序规则")
return True
finally:
self._release_lock()
# 破坏"循环等待"的进程类
class ProcessNoCircularWait:
def __init__(self, pid, required_res_ids):
self.pid = pid
# 强制排序:进程需要的资源必须按升序排列(用户传入乱序也会自动排序)
self.required_res_ids = sorted(required_res_ids)
self.status = "WAITING"
self.request_index = 0 # 当前申请到第几个资源
self.last_res_id = None # 上一次申请的资源ID(用于升序检查)
# 按升序申请资源
def request_resource(self, manager):
if self.status == "FINISHED" or self.request_index >= len(self.required_res_ids):
return True
current_res_id = self.required_res_ids[self.request_index]
# 申请资源(必须升序)
if manager.request_resource_ascending(self.pid, current_res_id, self.last_res_id):
self.last_res_id = current_res_id
self.request_index += 1
return True
return False
# 运行并释放资源
def run(self, manager):
if self.request_index < len(self.required_res_ids):
print(f"🟠 进程{self.pid}:未申请完所有资源,无法运行")
return
self.status = "RUNNING"
print(f"🟢 进程{self.pid}:开始执行任务(占用资源:{self.required_res_ids})")
time.sleep(random.uniform(0.5, 1.0))
# 释放所有资源
manager._check_lock()
try:
for res_id in self.required_res_ids:
res = manager.resources[res_id]
res.is_available = True
res.occupied_by = None
self.status = "FINISHED"
self.last_res_id = None
self.request_index = 0
print(f"🟣 进程{self.pid}:任务完成,释放所有资源")
finally:
manager._release_lock()
# 测试:破坏"循环等待"
def test_no_circular_wait():
# 1. 初始化资源(ID:0=打印机,1=CPU,2=内存,3=磁盘)
resources = [
Resource(0, "打印机"),
Resource(1, "CPU"),
Resource(2, "内存"),
Resource(3, "磁盘")
]
manager = AscendingResourceManager(resources)
# 2. 定义进程(故意传入乱序资源,测试升序强制排序)
# 进程0需要:内存(2)→ CPU(1)→ 打印机(0)(乱序,自动排序为0→1→2)
process0 = ProcessNoCircularWait(0, [2, 1, 0])
# 进程1需要:磁盘(3)→ 打印机(0)(乱序,自动排序为0→3)
process1 = ProcessNoCircularWait(1, [3, 0])
print("===== 测试:破坏循环等待(资源升序申请) =====")
# 进程0按升序申请:0→1→2
while not process0.request_resource(manager):
time.sleep(0.1)
while not process0.request_resource(manager):
time.sleep(0.1)
while not process0.request_resource(manager):
time.sleep(0.1)
# 进程1尝试逆序申请(先3后0),但被强制排序为0→3(0被进程0占用,等待)
process1.request_resource(manager)
print("\n当前资源状态:")
print(manager.get_resource_status())
# 进程0运行并释放资源
process0.run(manager)
# 进程1继续申请(此时0可用,按升序申请0→3)
process1.request_resource(manager)
process1.request_resource(manager)
process1.run(manager)
print("\n最终资源状态:")
print(manager.get_resource_status())
# 执行测试
test_no_circular_wait()
程序运行结果展示
bash
===== 测试:破坏循环等待(资源升序申请) =====
🔵 进程0:成功申请资源0(打印机),符合升序规则
🔵 进程0:成功申请资源1(CPU),符合升序规则
🔵 进程0:成功申请资源2(内存),符合升序规则
🟡 进程1:申请资源0(被进程0占用),等待
当前资源状态:
Resource(0, 打印机, 可用=False, 被进程0占用)
Resource(1, CPU, 可用=False, 被进程0占用)
Resource(2, 内存, 可用=False, 被进程0占用)
Resource(3, 磁盘, 可用=True, 被进程None占用)
🟢 进程0:开始执行任务(占用资源:[0, 1, 2])
🟣 进程0:任务完成,释放所有资源
🔵 进程1:成功申请资源0(打印机),符合升序规则
🔵 进程1:成功申请资源3(磁盘),符合升序规则
🟢 进程1:开始执行任务(占用资源:[0, 3])
🟣 进程1:任务完成,释放所有资源
最终资源状态:
Resource(0, 打印机, 可用=True, 被进程None占用)
Resource(1, CPU, 可用=True, 被进程None占用)
Resource(2, 内存, 可用=True, 被进程None占用)
Resource(3, 磁盘, 可用=True, 被进程None占用)
2. 死锁避免算法
- 原理 :不破坏死锁条件,而是在进程申请资源时预判 是否会导致死锁,若会则拒绝申请。核心是银行家算法。
- 银行家算法
- 核心思想:将操作系统比作银行家,资源比作贷款,进程申请资源时,系统计算分配后是否处于安全状态(存在一个进程执行序列,使得所有进程都能顺利完成)。
- 步骤:
- 初始化可用资源向量、最大需求矩阵、分配矩阵、需求矩阵。
- 进程申请资源时,判断需求是否小于等于剩余需求;若满足,尝试分配资源。
- 检查分配后系统是否安全,若安全则分配,否则拒绝。
- 特点:资源利用率高于死锁预防,但需要预知进程的最大资源需求,实现复杂。
Python代码
python
class BankerAlgorithm:
def __init__(self, available, max_demand, allocation):
"""
初始化银行家算法
:param available: 可用资源向量(列表)
:param max_demand: 最大需求矩阵(二维列表)
:param allocation: 分配矩阵(二维列表)
"""
self.n_processes = len(max_demand) # 进程数
self.n_resources = len(available) # 资源数
self.available = available.copy()
self.max_demand = [row.copy() for row in max_demand]
self.allocation = [row.copy() for row in allocation]
# 需求矩阵 = 最大需求 - 已分配
self.need = [
[self.max_demand[i][j] - self.allocation[i][j] for j in range(self.n_resources)]
for i in range(self.n_processes)
]
def is_safe(self):
"""检查系统是否处于安全状态"""
work = self.available.copy() # 工作向量
finish = [False] * self.n_processes # 进程完成标记
# 寻找可以完成的进程
while False in finish:
found = False
for i in range(self.n_processes):
if not finish[i] and all(self.need[i][j] <= work[j] for j in range(self.n_resources)):
# 分配资源给进程i,执行完成后释放
for j in range(self.n_resources):
work[j] += self.allocation[i][j]
finish[i] = True
found = True
if not found:
break # 没有找到可执行的进程,系统不安全
return all(finish)
def request_resources(self, pid, request):
"""
进程pid申请资源
:param pid: 进程ID
:param request: 申请的资源向量
:return: 是否允许分配
"""
# 1. 检查申请是否超过需求
if any(request[j] > self.need[pid][j] for j in range(self.n_resources)):
print(f"进程{pid}申请资源超过最大需求,拒绝分配")
return False
# 2. 检查申请是否超过可用资源
if any(request[j] > self.available[j] for j in range(self.n_resources)):
print(f"进程{pid}申请资源超过可用资源,需等待")
return False
# 3. 尝试分配
for j in range(self.n_resources):
self.available[j] -= request[j]
self.allocation[pid][j] += request[j]
self.need[pid][j] -= request[j]
# 4. 检查分配后是否安全
if self.is_safe():
print(f"进程{pid}资源申请成功,系统仍安全")
return True
else:
# 不安全则回滚
for j in range(self.n_resources):
self.available[j] += request[j]
self.allocation[pid][j] -= request[j]
self.need[pid][j] += request[j]
print(f"进程{pid}资源申请失败,分配后系统不安全")
return False
# 测试银行家算法
if __name__ == "__main__":
# 示例:3个进程,3类资源
available = [3, 3, 2]
max_demand = [
[7, 5, 3],
[3, 2, 2],
[9, 0, 2]
]
allocation = [
[0, 1, 0],
[2, 0, 0],
[3, 0, 2]
]
banker = BankerAlgorithm(available, max_demand, allocation)
print("初始系统是否安全:", banker.is_safe())
# 进程1申请资源 [1,0,2]
request1 = [1, 0, 2]
banker.request_resources(1, request1)
# 进程0申请资源 [0,2,0]
request2 = [0, 2, 0]
banker.request_resources(0, request2)
print("\n最终状态:")
print("可用资源:", banker.available)
print("需求矩阵:", banker.need)
程序运行结果展示
bash
初始系统是否安全: False
进程1资源申请失败,分配后系统不安全
进程0资源申请失败,分配后系统不安全
最终状态:
可用资源: [3, 3, 2]
需求矩阵: [[7, 4, 3], [1, 2, 2], [6, 0, 0]]
3. 死锁检测与解除算法
- 原理 :允许系统发生死锁,通过死锁检测算法 定期检测死锁,若检测到死锁,则通过死锁解除算法 恢复系统。
- 死锁检测 :构建资源分配图,通过化简资源分配图判断是否存在死锁(化简后存在孤立节点则无死锁,否则有死锁)。
- 死锁解除
- 资源剥夺:从死锁进程中剥夺资源分配给其他进程。
- 进程撤销:按一定策略撤销死锁进程(如撤销代价最小的进程),直到解除死锁。
- 进程回退:将死锁进程回退到之前的安全状态,重新执行。
- 特点:资源利用率最高,但检测和解除死锁需要额外开销。
死锁检测算法
核心逻辑
通过化简资源分配图,能完成的进程会释放资源,最终未完成的进程即为死锁进程。
Python代码
python
def detect_deadlock(processes, resources, allocation, request):
"""
死锁检测(资源分配图化简法)
:param processes: 进程列表
:param resources: 资源列表
:param allocation: 分配矩阵(进程→资源)
:param request: 请求矩阵(进程→资源)
:return: 死锁进程列表
"""
n_processes = len(processes)
n_resources = len(resources)
# 1. 初始化工作向量(可用资源)和完成标记
work = [0] * n_resources
for j in range(n_resources):
# 可用资源 = 总资源 - 已分配资源
total_resource = sum(allocation[i][j] for i in range(n_processes)) + work[j]
work[j] = total_resource - sum(allocation[i][j] for i in range(n_processes))
finish = [False] * n_processes
# 2. 化简资源分配图
while True:
found = False
for i in range(n_processes):
if not finish[i] and all(request[i][j] <= work[j] for j in range(n_resources)):
# 进程i可以完成,释放其占用的资源
for j in range(n_resources):
work[j] += allocation[i][j]
finish[i] = True
found = True
if not found:
break
# 3. 未完成的进程即为死锁进程
deadlock_processes = [processes[i] for i in range(n_processes) if not finish[i]]
return deadlock_processes
# 测试死锁检测
if __name__ == "__main__":
# 示例:4个进程,3类资源
processes = [0, 1, 2, 3]
resources = [0, 1, 2]
# 分配矩阵:process x resource
allocation = [
[0, 1, 0], # P0占用R1
[2, 0, 0], # P1占用R0*2
[3, 0, 2], # P2占用R0*3, R2*2
[2, 1, 1] # P3占用R0*2, R1*1, R2*1
]
# 请求矩阵:process x resource
request = [
[0, 0, 0], # P0无请求
[0, 0, 0], # P1无请求
[0, 0, 0], # P2无请求
[0, 0, 1] # P3请求R2*1(无可用资源,死锁)
]
deadlock = detect_deadlock(processes, resources, allocation, request)
print("死锁进程:", deadlock)
程序运行结果展示
bash
死锁进程: []
死锁解除算法
- 死锁检测:基于资源分配图化简法,通过判断进程是否能完成来识别死锁进程。
- 资源剥夺:从死锁进程中剥夺一个资源,分配给等待该资源的进程,打破死锁链。
- 进程撤销:选择撤销代价最小(如运行时间最短)的死锁进程,释放其所有资源。
- 进程回退:将死锁进程回退到之前保存的检查点,释放检查点后占用的资源,让系统回到安全状态。
Python代码
python
import time
import random
from collections import defaultdict
# ===================== 基础类定义 =====================
class Resource:
"""资源类:表示系统中的一个资源"""
def __init__(self, res_id, res_name):
self.id = res_id
self.name = res_name
self.is_available = True # 资源是否可用
self.occupied_by = None # 被哪个进程占用
def __repr__(self):
return f"资源{self.id}({self.name}): {'可用' if self.is_available else f'被进程{self.occupied_by}占用'}"
class Process:
"""进程类:表示系统中的一个进程"""
def __init__(self, pid, required_res_ids):
self.pid = pid
self.required_res_ids = required_res_ids # 进程需要的所有资源
self.owned_res_ids = [] # 已占用的资源
self.waiting_for = None # 等待的资源ID
self.status = "RUNNING" # 状态:RUNNING/WAITING/DEADLOCK/FINISHED
self.run_time = 0 # 已运行时间(用于计算撤销代价)
self.checkpoints = [] # 回退检查点(存储已占用资源的历史状态)
def save_checkpoint(self):
"""保存当前状态为回退检查点"""
self.checkpoints.append({
"owned_res_ids": self.owned_res_ids.copy(),
"waiting_for": self.waiting_for,
"status": self.status
})
def rollback_to_last_checkpoint(self):
"""回退到上一个检查点"""
if not self.checkpoints:
return False
checkpoint = self.checkpoints.pop()
self.owned_res_ids = checkpoint["owned_res_ids"]
self.waiting_for = checkpoint["waiting_for"]
self.status = checkpoint["status"]
return True
def __repr__(self):
return f"进程{self.pid}: 状态={self.status}, 已占用={self.owned_res_ids}, 等待={self.waiting_for}"
class ResourceManager:
"""资源管理器:管理资源分配、释放与死锁检测"""
def __init__(self, resources, processes):
self.resources = {res.id: res for res in resources}
self.processes = {p.pid: p for p in processes}
self.lock = False # 模拟原子操作锁
def _lock(self):
while self.lock:
time.sleep(0.001)
self.lock = True
def _unlock(self):
self.lock = False
def allocate_resource(self, pid, res_id):
"""分配资源给进程"""
self._lock()
try:
res = self.resources[res_id]
proc = self.processes[pid]
if res.is_available:
res.is_available = False
res.occupied_by = pid
proc.owned_res_ids.append(res_id)
proc.waiting_for = None
proc.status = "RUNNING"
print(f"🔵 进程{pid}成功获取资源{res_id}({res.name})")
return True
else:
proc.waiting_for = res_id
proc.status = "WAITING"
print(f"🟡 进程{pid}等待资源{res_id}({res.name})(被进程{res.occupied_by}占用)")
return False
finally:
self._unlock()
def release_resource(self, pid, res_id):
"""释放进程占用的资源"""
self._lock()
try:
res = self.resources[res_id]
proc = self.processes[pid]
if res.occupied_by == pid:
res.is_available = True
res.occupied_by = None
proc.owned_res_ids.remove(res_id)
print(f"🔓 进程{pid}释放资源{res_id}({res.name})")
return True
return False
finally:
self._unlock()
def detect_deadlock(self):
"""死锁检测:资源分配图化简法"""
self._lock()
try:
# 构建资源分配图:进程→资源的申请边和分配边
allocation = defaultdict(list) # 进程→已占资源
request = defaultdict(list) # 进程→等待资源
res_to_proc = {} # 资源→占用进程
for proc in self.processes.values():
for res_id in proc.owned_res_ids:
allocation[proc.pid].append(res_id)
res_to_proc[res_id] = proc.pid
if proc.waiting_for is not None:
request[proc.pid].append(proc.waiting_for)
# 化简资源分配图
work = {res.id: res.is_available for res in self.resources.values()}
finish = {proc.pid: False for proc in self.processes.values()}
while True:
found = False
for proc in self.processes.values():
if not finish[proc.pid] and all(work[res_id] for res_id in request[proc.pid]):
# 进程可以完成,释放资源
for res_id in allocation[proc.pid]:
work[res_id] = True
finish[proc.pid] = True
found = True
if not found:
break
# 未完成的进程即为死锁进程
deadlock_procs = [proc for proc in self.processes.values() if not finish[proc.pid]]
return deadlock_procs
finally:
self._unlock()
def deadlock_resolution_preempt(manager):
"""死锁解除:资源剥夺"""
deadlock_procs = manager.detect_deadlock()
if not deadlock_procs:
print("✅ 系统无死锁")
return
print(f"⚠️ 检测到死锁进程:{[p.pid for p in deadlock_procs]}")
print("🔧 执行资源剥夺...")
# 选择一个死锁进程,剥夺它的一个资源
target_proc = deadlock_procs[0]
if not target_proc.owned_res_ids:
print("❌ 无资源可剥夺,无法解除死锁")
return
# 剥夺一个资源
preempt_res_id = target_proc.owned_res_ids[0]
manager.release_resource(target_proc.pid, preempt_res_id)
target_proc.status = "WAITING"
# 尝试将剥夺的资源分配给等待该资源的进程
for proc in manager.processes.values():
if proc.status == "WAITING" and proc.waiting_for == preempt_res_id:
if manager.allocate_resource(proc.pid, preempt_res_id):
print(f"🔄 剥夺的资源{preempt_res_id}分配给进程{proc.pid},死锁解除")
break
def deadlock_resolution_abort(manager):
"""死锁解除:进程撤销(按运行时间代价最小优先)"""
deadlock_procs = manager.detect_deadlock()
if not deadlock_procs:
print("✅ 系统无死锁")
return
print(f"⚠️ 检测到死锁进程:{[p.pid for p in deadlock_procs]}")
print("🔧 执行进程撤销...")
# 按运行时间排序(运行时间越短,撤销代价越小)
deadlock_procs.sort(key=lambda p: p.run_time)
target_proc = deadlock_procs[0]
# 释放该进程占用的所有资源
for res_id in target_proc.owned_res_ids.copy():
manager.release_resource(target_proc.pid, res_id)
target_proc.status = "FINISHED"
print(f"🗑️ 撤销进程{target_proc.pid}(运行时间{target_proc.run_time},代价最小),死锁解除")
def deadlock_resolution_rollback(manager):
"""死锁解除:进程回退"""
deadlock_procs = manager.detect_deadlock()
if not deadlock_procs:
print("✅ 系统无死锁")
return
print(f"⚠️ 检测到死锁进程:{[p.pid for p in deadlock_procs]}")
print("🔧 执行进程回退...")
# 选择有检查点的死锁进程回退
target_proc = None
for proc in deadlock_procs:
if proc.checkpoints:
target_proc = proc
break
if not target_proc:
print("❌ 无检查点可回退,无法解除死锁")
return
# 回退到上一个检查点并释放资源
if target_proc.rollback_to_last_checkpoint():
# 释放回退点之后占用的资源
for res_id in target_proc.owned_res_ids:
manager.release_resource(target_proc.pid, res_id)
print(f"🔙 进程{target_proc.pid}回退到检查点,死锁解除")
def test_deadlock_resolution():
# 1. 初始化资源(0=打印机,1=CPU,2=内存,3=磁盘)
resources = [
Resource(0, "打印机"),
Resource(1, "CPU"),
Resource(2, "内存"),
Resource(3, "磁盘")
]
# 2. 初始化进程(构造死锁场景:进程0占CPU等内存,进程1占内存等CPU)
process0 = Process(0, [1, 2]) # 需要CPU+内存
process1 = Process(1, [2, 1]) # 需要内存+CPU
process0.save_checkpoint() # 保存初始检查点
process1.save_checkpoint()
processes = [process0, process1]
manager = ResourceManager(resources, processes)
# 3. 构造死锁
print("===== 构造死锁场景 =====")
manager.allocate_resource(0, 1) # 进程0获取CPU
manager.allocate_resource(1, 2) # 进程1获取内存
manager.allocate_resource(0, 2) # 进程0等待内存
manager.allocate_resource(1, 1) # 进程1等待CPU
# 4. 检测死锁
deadlock_procs = manager.detect_deadlock()
print(f"死锁检测结果:{[p.pid for p in deadlock_procs]}")
# 5. 测试三种解除方式
print("\n===== 测试1:资源剥夺 =====")
deadlock_resolution_preempt(manager)
print("\n===== 测试2:进程撤销 =====")
deadlock_resolution_abort(manager)
print("\n===== 测试3:进程回退 =====")
deadlock_resolution_rollback(manager)
# 6. 验证死锁解除
after_deadlock = manager.detect_deadlock()
print(f"死锁解除后检测:{[p.pid for p in after_deadlock] if after_deadlock else '无死锁'}")
# 执行测试
test_deadlock_resolution()
程序运行结果展示
bash
===== 构造死锁场景 =====
🔵 进程0成功获取资源1(CPU)
🔵 进程1成功获取资源2(内存)
🟡 进程0等待资源2(内存)(被进程1占用)
🟡 进程1等待资源1(CPU)(被进程0占用)
死锁检测结果:[0, 1]
===== 测试1:资源剥夺 =====
⚠️ 检测到死锁进程:[0, 1]
🔧 执行资源剥夺...
🔓 进程0释放资源1(CPU)
🔵 进程1成功获取资源1(CPU)
🔄 剥夺的资源1分配给进程1,死锁解除
===== 测试2:进程撤销 =====
✅ 系统无死锁
===== 测试3:进程回退 =====
✅ 系统无死锁
死锁解除后检测:无死锁
五、 各类算法的对比与选型
| 算法类别 | 典型算法 | 核心优势 | 适用场景 |
|---|---|---|---|
| 进程调度 | 多级反馈队列 | 兼顾公平与效率 | 通用操作系统 |
| 进程调度 | 优先级调度(抢占式) | 支持实时任务 | 实时操作系统 |
| 同步互斥 | 信号量(PV 操作) | 灵活通用 | 各类同步互斥场景 |
| 同步互斥 | 管程 | 易于维护,安全性高 | 面向对象的编程场景 |
| 死锁处理 | 银行家算法 | 资源利用率高 | 资源需求可预估的系统 |
| 死锁处理 | 资源有序分配 | 实现简单 | 批处理系统 |
六、总结
本文系统介绍了操作系统中的三大核心算法:进程调度算法、进程同步与互斥算法以及死锁处理算法。通过Python代码实现了先来先服务(FCFS)、短作业优先(SJF)、优先级调度、时间片轮转(RR)和多级反馈队列等进程调度算法;详细讲解了信号量、管程等同步机制,并实现了生产者-消费者、读者-写者和哲学家进餐等经典问题;针对死锁问题,阐述了预防、避免、检测与解除四种处理策略。文章还提供了各类算法的对比选型建议,为操作系统资源管理提供了全面的技术参考。所有算法均配有可执行的Python实现和运行结果展示。