【操作系统】进程的算法

目录

一、引言

[二、 进程调度算法](#二、 进程调度算法)

[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. 设置多个就绪队列,每个队列的优先级从高到低,时间片长度从短到长(如第 1 队列时间片 10ms,第 2 队列 20ms,依次翻倍)。
    2. 新进程进入第 1 队列,按 RR 调度;时间片用完未完成,则降到下一级队列尾部。
    3. 高优先级队列的进程可抢占低优先级队列的 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 个条件:互斥性 (同一时刻只有一个进程在临界区)、空闲让进 (临界区空闲时允许进程进入)、有限等待(进程等待进入临界区的时间有限)。

  • 软件实现算法
    1. Peterson 算法 :适用于两个进程的互斥,通过turn(轮到哪个进程)和flag(进程是否想进入临界区)两个变量实现,满足临界区的 3 个条件。
    2. 面包店算法:适用于多个进程的互斥,模拟面包店取号排队的机制,实现复杂但支持任意数量进程。
  • 硬件实现方法
    1. 关中断:进程进入临界区前关闭中断,避免进程切换,简单但会导致系统效率下降(单核有效)。
    2. Test-and-Set 指令(TS 指令):原子操作,通过指令判断并设置锁变量,实现互斥。
    3. 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

软件实现算法:面包店算法

面包店算法是软件实现的多进程互斥算法,模拟面包店 "取号 - 排队" 机制,满足临界区的三个核心条件(互斥性、空闲让进、有限等待):

  1. 每个线程进入临界区前,先 "取号"(number[tid] = max(number) + 1);
  2. 取号时用choosing[tid]标记 "正在取号",避免多个线程同时取到相同的号;
  3. 取号后,线程检查所有其他线程:只有当自己的号是 "最小号"(或同号但 ID 更小),才能进入临界区;
  4. 退出临界区时,释放自己的号(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 下的简单互斥方法

  1. 进程进入临界区前,关闭 CPU 中断(屏蔽时钟中断);
  2. 因为没有中断,CPU 不会切换进程,临界区代码独占执行;
  3. 退出临界区后,打开中断,恢复进程调度。⚠️ 缺点:单核有效,多核失效;关中断时间过长会导致系统响应慢、效率下降。

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

互斥实现逻辑:

  1. 共享锁变量lock(初始False:空闲);
  2. 进程私有变量key(设为True:表示 "想要锁");
  3. 原子交换keylock
    • 若交换后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()  # 唤醒一个等待的线程

生产者 - 消费者问题

问题背景

  • 生产者进程:持续生产产品,放入有限大小的缓冲区
  • 消费者进程:持续从缓冲区取出产品消费;
  • 核心约束:
    1. 缓冲区满时,生产者不能生产(同步约束);
    2. 缓冲区空时,消费者不能消费(同步约束);
    3. 同一时刻,只有一个进程(生产者 / 消费者)操作缓冲区(互斥约束)。

信号量设计

信号量 初始值 作用
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 个

读者 - 写者问题

问题背景

多个读者可以同时读取共享资源,但写者需要独占资源;读者和写者互斥,写者和写者互斥。读者优先 是指:只要有读者在读取,写者就必须等待;即使写者先申请,后续到达的读者仍会优先获得资源。

核心思路

  1. read_count 记录当前正在读取的读者数量;
  2. mutex 信号量(初始值 1):保护 read_count 的互斥访问(读者进入 / 退出时修改该变量);
  3. rw_mutex 信号量(初始值 1):实现读者 / 写者、写者 / 写者的互斥(只有第一个读者会 P 操作,最后一个读者会 V 操作);
  4. 写者直接操作 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 个哲学家同时拿筷子」的策略:

  1. chopsticks 列表表示 5 根筷子(每个元素是信号量,初始值 1);
  2. eating_allowance 信号量(初始值 4):限制同时拿筷子的哲学家数量(最多 4 个),避免 5 人同时拿左手筷子导致死锁;
  3. 哲学家拿筷子顺序:先申请「允许进餐」→ 拿左手筷子 → 拿右手筷子 → 进餐 → 放下筷子 → 释放「允许进餐」。

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. 破坏 "请求与保持":进程一次性申请所有需要的资源,运行过程中不再申请新资源。
    2. 破坏 "不剥夺":进程申请新资源失败时,主动释放已占有的资源。
    3. 破坏 "循环等待":对资源进行编号,进程必须按升序顺序申请资源。
  • 特点:实现简单,但资源利用率低,系统吞吐量下降。

算法 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. 死锁避免算法

  • 原理 :不破坏死锁条件,而是在进程申请资源时预判 是否会导致死锁,若会则拒绝申请。核心是银行家算法
  • 银行家算法
    1. 核心思想:将操作系统比作银行家,资源比作贷款,进程申请资源时,系统计算分配后是否处于安全状态(存在一个进程执行序列,使得所有进程都能顺利完成)。
    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. 死锁检测与解除算法

  • 原理 :允许系统发生死锁,通过死锁检测算法 定期检测死锁,若检测到死锁,则通过死锁解除算法 恢复系统。
    1. 死锁检测 :构建资源分配图,通过化简资源分配图判断是否存在死锁(化简后存在孤立节点则无死锁,否则有死锁)。
    2. 死锁解除
      • 资源剥夺:从死锁进程中剥夺资源分配给其他进程。
      • 进程撤销:按一定策略撤销死锁进程(如撤销代价最小的进程),直到解除死锁。
      • 进程回退:将死锁进程回退到之前的安全状态,重新执行。
  • 特点:资源利用率最高,但检测和解除死锁需要额外开销。

死锁检测算法

核心逻辑

通过化简资源分配图,能完成的进程会释放资源,最终未完成的进程即为死锁进程。

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实现和运行结果展示。

相关推荐
Tansmjs2 小时前
使用Pandas进行数据分析:从数据清洗到可视化
jvm·数据库·python
ASD123asfadxv2 小时前
【目标检测】YOLOv26:基于改进算法的乌鸦识别系统详解
算法·yolo·目标检测
夏幻灵2 小时前
Java中的this关键字解析与应用
java·开发语言·python
溜达的大象2 小时前
BEV感知算法技术演进之路:从传感器标定到端到端模型应用
算法
有时间要学习2 小时前
面试150——第四周
算法·面试
LetsonH2 小时前
Swap 大小一键调整脚本
人工智能·python
难得的我们2 小时前
C++中的状态模式
开发语言·c++·算法
xhbaitxl2 小时前
算法学习day27-贪心算法
学习·算法·贪心算法
啊阿狸不会拉杆2 小时前
《计算机操作系统》第十章 - 多处理机操作系统
c++·算法·计算机组成原理·os·计算机操作系统