Python实现电梯调度进程模型:FCFS+状态机仿真

发布时间:2026/9/16 15:16:16
Python实现电梯调度进程模型:FCFS+状态机仿真 简介本资源是一份面向计算机专业本科生的课程设计级Python实践项目聚焦电梯进程建模与调度算法实现适用于操作系统、算法设计或GUI应用开发等课程学习与实训。压缩包共36个文件含3个核心Python源码myElevator.py、dispatch.py、myElevatorInterface.py、1份完整Word版设计方案报告、18张UI界面截图涵盖按钮状态、运行指示、楼层状态等、1个PyQt5图标资源及LICENSE等辅助文件整体19.32MB结构清晰模块职责分明——调度逻辑、界面交互、状态管理分层解耦。已有668人学习下载读者可直接运行PyQt5 GUI程序观察多线程模拟的电梯响应过程结合设计报告深入理解FCFS、SCAN等调度策略在进程抽象中的映射实现并参考PNG素材快速复现界面效果是理论联系实际、从代码到可视化的完整教学闭环案例。1. 用 Python 模拟电梯调度不是写个动画而是建一个可验证的进程管理模型很多人看到“电梯调度”第一反应是画个带楼层的 GUI 界面拖动小方块上下跑——那只是演示不是建模。真正有价值的电梯调度模拟核心在于把「请求生成→就绪队列组织→CPU即电梯轿厢资源分配→执行时长控制→状态迁移」这一整套进程生命周期用操作系统级逻辑映射到物理电梯行为上。它本质是一个单处理器、非抢占式、FCFS 就绪队列、带 I/O 阻塞开门/关门/加减速的受限进程调度器。你不需要控制真实电梯但必须让每个“乘客请求”表现为一个有创建时间、目标楼层、等待时长、服务时长、阻塞状态的进程实体让“电梯”本身是一个独占资源的调度主体能按规则从就绪队列取任务、执行、切换、响应中断如新召唤按钮按下。这个模型对理解 Linux 进程调度、容器编排中的资源抢占、甚至 AGV 调度系统底层逻辑都有直接迁移价值。适合刚学完操作系统原理想动手验证的学生也适合需要快速构建轻量级调度原型的嵌入式或 IoT 开发者。2. 为什么选 Python 而不是 C 或 Rust关键在三类对象的抽象与解耦2.1 电梯调度模型的三层抽象请求、进程、调度器必须分离真实电梯系统中“某人在 3 楼按了上行键”是一个外部事件它触发系统创建一个服务请求该请求被接纳后会绑定到某个电梯轿厢的执行上下文中形成一个具有生命周期的“服务进程”而多个进程如何排队、谁先走、是否被中断则由独立的调度器决策。这三层不能揉在一起写成一个 while 循环。Python 的类封装和弱类型特性恰好支持这种清晰分层ElevatorRequest类只管原始输入timestamp毫秒级创建时间、source_floor、target_floor、directionUP/DOWN、is_internal轿厢内按钮ElevatorProcess类继承自threading.Thread但重写run()为纯逻辑执行不真启线程包含stateREADY/RUNNING/BLOCKED/TERMINATED、remaining_time模拟运行耗时、wait_time就绪队列等待时长ElevatorScheduler类持有就绪队列deque、运行中进程引用、电梯当前状态current_floor,direction,is_moving并实现schedule()方法提示这里不用multiprocessing.Process因为真实电梯是单体设备不存在跨核并行也不用asyncio因 FCFS 是确定性调度无需协程切换开销。用threading.Thread子类仅作状态容器避免引入真实并发干扰调度逻辑验证。2.2 就绪队列必须用双端队列deque且严格按 FCFS 插入FCFSFirst-Come, First-Served不是简单排序而是插入顺序即执行顺序。若用list.append()list.pop(0)每次弹出首元素都是 O(n) 时间复杂度当请求量达千级时调度延迟会失真。必须用collections.deque其append()和popleft()均为 O(1)from collections import deque class ElevatorScheduler: def __init__(self): self.ready_queue deque() # 关键用 deque 实现 O(1) FCFS 队列 self.running_process None self.current_floor 1 self.direction IDLE def add_request(self, req: ElevatorRequest): # 创建对应进程并按到达时间顺序入队 proc ElevatorProcess(req) self.ready_queue.append(proc) # O(1) 插入尾部2.2.1 为什么不能用 heapq 或 sorted listheapq默认最小堆若按req.timestamp排序看似合理但 FCFS 要求完全忽略优先级只认到达先后。一旦两个请求timestamp相同毫秒级精度下极易发生heapq的稳定排序无法保证插入顺序会导致调度结果不可复现。而deque天然保序且无比较开销。2.3 进程状态机必须显式定义且阻塞态需区分类型电梯进程的阻塞不是简单的time.sleep()它分三种可观察类型阻塞类型触发条件持续时间可否被抢占对调度器影响DOOR_OPENING到达目标楼层前固定 2s否物理限制调度器必须等待不处理新请求DOOR_CLOSING开门结束后固定 1.5s否同上ACCELERATION/DECELERATION启动/停止瞬间与距离相关每层 0.8s否调度器需计算剩余移动时间因此ElevatorProcess.state必须是枚举值且ElevatorScheduler.schedule()中需根据当前状态决定下一步动作from enum import Enum class ProcessState(Enum): READY READY RUNNING RUNNING BLOCKED_DOOR_OPEN BLOCKED_DOOR_OPEN BLOCKED_DOOR_CLOSE BLOCKED_DOOR_CLOSE BLOCKED_ACCEL BLOCKED_ACCEL TERMINATED TERMINATED # 在 scheduler.schedule() 中 if self.running_process and self.running_process.state ProcessState.BLOCKED_DOOR_OPEN: self.running_process.remaining_time - 1 if self.running_process.remaining_time 0: self.running_process.state ProcessState.BLOCKED_DOOR_CLOSE self.running_process.remaining_time 1500 # 1.5s in ms2.3.1 参数表电梯物理行为的关键毫秒级常量参数名含义推荐值ms说明DOOR_OPEN_TIME开门耗时2000从门开始动到全开DOOR_CLOSE_TIME关门耗时1500从门开始关到全闭FLOOR_TRAVEL_TIME层间匀速运行时间1200不含加减速仅中间段ACCEL_DECEL_TIME_PER_FLOOR每层加/减速耗时800启动加速或制动减速阶段MAX_WAIT_BEFORE_TIMEOUT就绪队列最大等待阈值30000超过 30s 未调度则标记异常这些值不是随意设的而是参照 GB/T 10058-2009《电梯技术条件》中对客梯的典型参数额定速度 1.75m/s层高 3m加减速度 0.8m/s²由此反推各阶段时间。3. 用 60 行核心代码跑通最小可验证调度循环3.1 调度主循环时间片驱动而非事件驱动电梯调度不是“一有请求就立刻响应”而是以固定时间片如 100ms推进仿真时钟。这才能准确测量等待时间、服务时间、吞吐量。主循环结构如下import time from datetime import datetime def run_simulation(scheduler: ElevatorScheduler, duration_ms: int 60000): start_time int(time.time() * 1000) current_ms start_time # 模拟 60 秒运行 while (current_ms - start_time) duration_ms: # 步骤1生成新请求每 5s 随机一个 if (current_ms - start_time) % 5000 100: # 每 5s 一次 floor random.randint(1, 15) target random.randint(1, 15) while target floor: target random.randint(1, 15) req ElevatorRequest( timestampcurrent_ms, source_floorfloor, target_floortarget, directionUP if target floor else DOWN ) scheduler.add_request(req) # 步骤2调度器执行一个时间片100ms scheduler.tick(delta_ms100) # 步骤3记录关键指标每秒打印一次 if (current_ms - start_time) % 1000 100: stats scheduler.get_stats() print(f[{datetime.now().strftime(%H:%M:%S)}] fQueue:{len(scheduler.ready_queue)} fRunning:{scheduler.running_process.state if scheduler.running_process else IDLE} fAvgWait:{stats[avg_wait_ms]:.0f}ms) current_ms 100 time.sleep(0.1) # 真实延时便于观察3.1.1scheduler.tick()的四步原子操作该方法是整个模型的心脏必须在一个时间片内完成全部状态更新检查运行中进程是否结束若running_process.remaining_time 0将其置为TERMINATED并清空引用检查就绪队列是否有新进程可调度若running_process is None且ready_queue非空则popleft()取出首个进程设置其stateRUNNING初始化remaining_time推进运行中进程的时间根据其当前state扣减对应remaining_time如BLOCKED_DOOR_OPEN扣 100ms处理电梯物理状态同步若进程进入BLOCKED_DOOR_OPEN则更新scheduler.current_floor为目标楼层若进入BLOCKED_ACCEL则按方向更新current_floor向上1向下-1。注意tick()内不能有time.sleep()否则会破坏时间片精度所有延时都在主循环time.sleep(0.1)中统一控制。3.2 进程创建与就绪队列注入用工厂函数封装规则不是所有请求都立即进就绪队列。真实电梯有“服务方向一致性”约束若电梯正上行且在 5 楼它不会响应 3 楼的上行请求因已过站但会响应 7 楼的上行请求。因此add_request()需调用策略函数判断是否接纳def should_accept_request(self, req: ElevatorRequest) - bool: if self.direction IDLE: return True # 空闲时接所有 elif self.direction UP: return (req.source_floor self.current_floor and req.direction UP) or \ (req.target_floor self.current_floor and req.direction DOWN) # 下行请求但目标在上方可捎带 else: # DOWN return (req.source_floor self.current_floor and req.direction DOWN) or \ (req.target_floor self.current_floor and req.direction UP)该逻辑直接对应电梯群控系统中的“区域划分方向锁定”策略是 FCFS 在物理约束下的必要增强。4. 验证调度正确性的三个硬指标必须写进日志并导出 CSV4.1 每个进程必须记录完整生命周期时间戳光看控制台打印不够。每个ElevatorProcess实例需在创建、入队、开始执行、开门、关门、终止时打点记录毫秒级时间戳。最终生成结构化日志# 在 ElevatorProcess.__init__() 中 self.timestamps { created: req.timestamp, enqueued: None, started: None, door_opened: None, door_closed: None, terminated: None } # 在 scheduler.tick() 中更新 if proc.state ProcessState.RUNNING and proc.timestamps[started] is None: proc.timestamps[started] current_ms if proc.state ProcessState.BLOCKED_DOOR_OPEN and proc.timestamps[door_opened] is None: proc.timestamps[door_opened] current_ms # ... 其他状态同理4.1.1 关键指标计算公式必须可复现指标名计算公式业务含义wait_time_mstimestamps[started] - timestamps[enqueued]就绪队列等待时长反映调度公平性service_time_mstimestamps[terminated] - timestamps[started]真实服务耗时含所有阻塞response_time_mstimestamps[door_opened] - timestamps[created]用户从按键到门开的总延迟体验核心指标throughput_per_min(completed_count / total_duration_minutes)每分钟完成请求数系统吞吐能力4.2 用 pandas 导出分析 CSV一行命令查瓶颈将所有进程日志转为 DataFrame用一行命令定位超时请求import pandas as pd def export_stats_to_csv(processes: List[ElevatorProcess], filename: str): records [] for p in processes: if p.timestamps[terminated]: records.append({ pid: id(p), created: p.timestamps[created], enqueued: p.timestamps[enqueued] or 0, started: p.timestamps[started] or 0, door_opened: p.timestamps[door_opened] or 0, terminated: p.timestamps[terminated], wait_time_ms: p.wait_time_ms, service_time_ms: p.service_time_ms, response_time_ms: p.response_time_ms, is_timeout: p.response_time_ms 30000 # 超 30s 标红 }) df pd.DataFrame(records) df.to_csv(filename, indexFalse) # 终端执行 # $ python -c import pandas as pd; dfpd.read_csv(elevator_log.csv); print(df[df.is_timeout])4.2.1 常见瓶颈场景及修复指令场景日志特征修复方式命令示例就绪队列积压wait_time_ms持续 5000ms缩短DOOR_OPEN_TIME或增加电梯数改scheduler实例数sed -i s/DOOR_OPEN_TIME 2000/DOOR_OPEN_TIME 1500/ elevator.py响应延迟突增response_time_ms在某时间点集体跳升检查该时段是否集中涌入同向请求启用 LOOK 调度替代 FCFSgit checkout elevator_scheduler_look.py进程状态卡死多个进程state长期为BLOCKED_DOOR_OPENremaining_time未正确扣减检查tick()中状态转移逻辑grep -n BLOCKED_DOOR_OPEN elevator.py5. 进阶技巧用信号量模拟多电梯协同与请求抢占5.1 单调度器扩展为多实例用 threading.Event 控制全局状态真实建筑有不止一部电梯。要模拟两台电梯协同不能简单起两个ElevatorScheduler实例——它们会竞争同一组请求。必须引入中央协调器import threading class MultiElevatorCoordinator: def __init__(self, elevators: List[ElevatorScheduler]): self.elevators elevators self.request_lock threading.Lock() self.global_ready_queue deque() self.coordinator_thread threading.Thread(targetself._coordinator_loop) self.coordinator_thread.daemon True self.coordinator_thread.start() def _coordinator_loop(self): while True: # 每 200ms 扫描一次全局队列 time.sleep(0.2) with self.request_lock: if self.global_ready_queue: # 策略分配给离请求源楼层最近的空闲电梯 req self.global_ready_queue.popleft() best_elevator min( self.elevators, keylambda e: abs(e.current_floor - req.source_floor) ) best_elevator.add_request(req)5.1.1 抢占式调度的轻量实现用优先队列替换 deque若需支持“消防员模式”紧急请求插队只需将ready_queue从deque换成heapq并让ElevatorRequest.priority成为堆排序键import heapq class PriorityElevatorScheduler(ElevatorScheduler): def __init__(self): super().__init__() self.ready_queue [] # 改为 list用 heapq 维护 def add_request(self, req: ElevatorRequest): # priority: 0紧急, 1普通, 2预约 heapq.heappush(self.ready_queue, (req.priority, req.timestamp, ElevatorProcess(req))) def get_next_request(self): if self.ready_queue: _, _, proc heapq.heappop(self.ready_queue) # 弹出最高优先级 return proc提示heapq元组排序规则是依次比对(0, 1000, proc)一定排在(1, 500, proc)前确保紧急请求绝对优先。5.2 用 logging 模块替代 print支持实时日志流分析控制台打印无法做聚合分析。将所有调度事件接入logging并配置RotatingFileHandlerimport logging from logging.handlers import RotatingFileHandler def setup_logger(): logger logging.getLogger(elevator) logger.setLevel(logging.INFO) handler RotatingFileHandler( elevator_runtime.log, maxBytes10*1024*1024, # 10MB backupCount5 ) formatter logging.Formatter(%(asctime)s - %(levelname)s - %(message)s) handler.setFormatter(formatter) logger.addHandler(handler) return logger # 在 scheduler.tick() 中 logger.info(fPROCESS[{proc.pid}] STARTED at floor {scheduler.current_floor}) logger.debug(fQUEUE LENGTH: {len(scheduler.ready_queue)})然后用tail -f elevator_runtime.log | grep STARTED | wc -l实时统计每秒启动进程数这是压测时最直观的吞吐监控手段。5.3 验证调度策略有效性的黄金命令用 awk 统计响应时间分布不依赖 Python用 shell 命令快速验证# 从日志提取所有 response_time_ms 字段假设格式... response_time_ms12450 ... awk {for(i1;iNF;i) if($i ~ /response_time_ms/) {split($i,a,); print a[2]}} elevator_runtime.log | \ sort -n | \ awk BEGIN{count0;sum0;min999999;max0} {count; sum$1; if($1min)min$1; if($1max)max$1} END{print COUNT:count,AVG:int(sum/count),MIN:min,MAX:max,P95:int(count*0.95)th} | \ column -t -s 这条命令输出COUNT: 142 AVG: 8420 MIN: 2100 MAX: 42500 P95: 137th直接告诉你 95% 的请求响应时间在第 137 个样本值以下——这才是生产环境真正关心的 SLO 指标。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询