Simpy医院系统模拟资源请求异常:患者数量统计不符问题修复
医院模拟系统Simpy资源请求问题修复
我是Simpy新手,开发简易医院模拟系统时遇到资源请求逻辑错误:模拟结束后总就诊患者数大于已诊断患者数+候诊患者数之和。系统逻辑为患者持续进入候诊室,多名医生处理候诊队列,接诊后花费时间诊断。怀疑HospitalManager中候诊室与医生的资源请求逻辑有误,求修复代码。
原代码如下:
import random from dataclasses import dataclass from datetime import datetime from uuid import UUID, uuid4 from faker import Faker from simpy import Environment, Resource, Store @dataclass class Patient: id: UUID created_at: int | float name: str address: str sex: str birthdate: datetime class WaitingRoom: def __init__(self, env: Environment): self._patients = Store(env) def put(self, patient: Patient): self._patients.put(patient) def get(self): return self._patients.get() class DoctorManager: def __init__(self, env: Environment, num_doctors: int, diagnosis_time: float): self._doctors = Resource(env, capacity=num_doctors) self._diagnosis_time = diagnosis_time def diagnose(self, env: Environment, patient: Patient): yield env.timeout(random.expovariate(1.0 / self._diagnosis_time)) print(env.now, patient, "is diagnosed.") class HospitalManager: def __init__(self, waiting_room: WaitingRoom, doctor_manager: DoctorManager): self._waiting_room = waiting_room self._doctor_manager = doctor_manager self._diagnosed_patients_number = 0 def add_patient_to_waiting_room(self, patient: Patient): self._waiting_room.put(patient) def invite_patient_to_doctor(self, env: Environment): with self._waiting_room.get() as patient_req: with self._doctor_manager._doctors.request() as doctor_req: yield doctor_req yield patient_req yield env.process(self._doctor_manager.diagnose(env, patient_req.value)) self._diagnosed_patients_number += 1 def keep_inviting_patient_to_doctor(self, env: Environment): while True: yield env.timeout(1) env.process(self.invite_patient_to_doctor(env)) def monitor_waiting_room(self, env: Environment): while True: print( env.now, len(self._waiting_room._patients.items), "patients are waiting", ) print(f"Diagnosed patients: {self._diagnosed_patients_number}") yield env.timeout(5) class Ecosystem: def __init__(self, patient_visit_time: float, hospital_manager: HospitalManager): self._patient_visit_time = patient_visit_time self._hospital_manager = hospital_manager self._patient_provider = Faker() def run(self, env: Environment, until): env.process(self._continue_to_generate_patients(env)) env.process(self._hospital_manager.keep_inviting_patient_to_doctor(env)) env.process(self._hospital_manager.monitor_waiting_room(env)) env.run(until=until) def _generate_patient(self, env: Environment): profile = self._patient_provider.simple_profile() return Patient( id=uuid4(), created_at=env.now, name=profile["name"], address=profile["address"], sex=profile["sex"], birthdate=profile["birthdate"], ) def _continue_to_generate_patients(self, env: Environment): patient_number = 0 while True: yield env.timeout(random.expovariate(1.0 / self._patient_visit_time)) patient_number += 1 print(f"Total Visiting Patients: {patient_number}") patient = self._generate_patient(env) self._hospital_manager.add_patient_to_waiting_room(patient) if __name__ == "__main__": env = Environment() waiting_room = WaitingRoom(env) doctor_manager = DoctorManager(env, 3, 10) hospital_manager = HospitalManager(waiting_room, doctor_manager) ecosystem = Ecosystem(1, hospital_manager) ecosystem.run(env, 1000)
问题分析
- 资源请求顺序错误:
invite_patient_to_doctor中先请求医生资源,再获取候诊患者。这会导致医生资源被占用后,若候诊室无患者,医生会被空耗;同时,当模拟结束时,部分已被取走候诊室但未完成诊断的患者,既不在候诊室统计中,也未计入已诊断数,导致总数不符。 - 冗余进程堆积:
keep_inviting_patient_to_doctor每隔1秒启动新的接诊进程,会导致大量进程等待资源,加剧患者“丢失”的情况,同时浪费系统资源。
修复后代码
import random from dataclasses import dataclass from datetime import datetime from uuid import UUID, uuid4 from faker import Faker from simpy import Environment, Resource, Store @dataclass class Patient: id: UUID created_at: int | float name: str address: str sex: str birthdate: datetime class WaitingRoom: def __init__(self, env: Environment): self._patients = Store(env) def put(self, patient: Patient): self._patients.put(patient) def get(self): return self._patients.get() @property def waiting_count(self): # 提供对外的候诊人数统计接口,避免直接访问私有属性 return len(self._patients.items) class DoctorManager: def __init__(self, env: Environment, num_doctors: int, diagnosis_time: float): self._doctors = Resource(env, capacity=num_doctors) self._diagnosis_time = diagnosis_time def diagnose(self, env: Environment, patient: Patient): yield env.timeout(random.expovariate(1.0 / self._diagnosis_time)) print(f"{env.now} - {patient.name} is diagnosed.") class HospitalManager: def __init__(self, waiting_room: WaitingRoom, doctor_manager: DoctorManager): self._waiting_room = waiting_room self._doctor_manager = doctor_manager self._diagnosed_patients_number = 0 self._diagnosing_patients_number = 0 # 新增:统计正在诊断的患者数 def add_patient_to_waiting_room(self, patient: Patient): self._waiting_room.put(patient) def process_patient(self, env: Environment): # 重构接诊逻辑:先取患者,再请求医生,避免资源空占 while True: # 先获取候诊患者,无患者则等待 patient = yield self._waiting_room.get() # 请求医生资源,有空闲医生则开始诊断 with self._doctor_manager._doctors.request() as doctor_req: yield doctor_req self._diagnosing_patients_number += 1 yield env.process(self._doctor_manager.diagnose(env, patient)) self._diagnosing_patients_number -= 1 self._diagnosed_patients_number += 1 def start_doctor_processes(self, env: Environment): # 根据医生数量启动对应数量的接诊进程,避免冗余进程 for _ in range(self._doctor_manager._doctors.capacity): env.process(self.process_patient(env)) def monitor_waiting_room(self, env: Environment): while True: waiting = self._waiting_room.waiting_count diagnosing = self._diagnosing_patients_number diagnosed = self._diagnosed_patients_number print(f"{env.now} - Waiting: {waiting}, Diagnosing: {diagnosing}, Diagnosed: {diagnosed}") print(f"Total tracked patients: {waiting + diagnosing + diagnosed}") yield env.timeout(5) class Ecosystem: def __init__(self, patient_visit_time: float, hospital_manager: HospitalManager): self._patient_visit_time = patient_visit_time self._hospital_manager = hospital_manager self._patient_provider = Faker() self._total_patients = 0 # 统一统计总就诊数,避免之前的局部变量问题 def run(self, env: Environment, until): env.process(self._continue_to_generate_patients(env)) self._hospital_manager.start_doctor_processes(env) env.process(self._hospital_manager.monitor_waiting_room(env)) env.run(until=until) # 模拟结束后打印最终统计 waiting = self._hospital_manager._waiting_room.waiting_count diagnosing = self._hospital_manager._diagnosing_patients_number diagnosed = self._hospital_manager._diagnosed_patients_number print(f"\n=== Simulation End ===") print(f"Total Visiting Patients: {self._total_patients}") print(f"Waiting: {waiting}, Diagnosing: {diagnosing}, Diagnosed: {diagnosed}") print(f"Total Tracked: {waiting + diagnosing + diagnosed}") def _generate_patient(self, env: Environment): profile = self._patient_provider.simple_profile() return Patient( id=uuid4(), created_at=env.now, name=profile["name"], address=profile["address"], sex=profile["sex"], birthdate=profile["birthdate"], ) def _continue_to_generate_patients(self, env: Environment): while True: yield env.timeout(random.expovariate(1.0 / self._patient_visit_time)) self._total_patients += 1 print(f"{env.now} - New patient arrived! Total: {self._total_patients}") patient = self._generate_patient(env) self._hospital_manager.add_patient_to_waiting_room(patient) if __name__ == "__main__": env = Environment() waiting_room = WaitingRoom(env) doctor_manager = DoctorManager(env, 3, 10) hospital_manager = HospitalManager(waiting_room, doctor_manager) ecosystem = Ecosystem(1, hospital_manager) ecosystem.run(env, 1000)
关键修改说明
- 调整资源获取顺序:将接诊逻辑改为先获取候诊患者,再请求医生资源,确保只有当有患者时才占用医生,避免资源空耗,同时保证患者不会被提前取走而未被统计。
- 优化进程启动逻辑:根据医生数量启动对应数量的接诊进程,每个进程循环处理患者,避免原代码中频繁启动新进程导致的冗余堆积。
- 完善统计维度:新增正在诊断的患者统计,模拟结束时统一打印总就诊数、候诊数、诊断中数、已诊断数,确保总数完全匹配。
- 封装统计接口:给
WaitingRoom添加waiting_count属性,避免直接访问私有属性,提高代码封装性。 - 统一总就诊数统计:将总就诊数移到
Ecosystem的成员变量中,避免原代码局部变量可能导致的统计误差。
内容的提问来源于stack exchange,提问作者Shuhei Kishi
相关产品推荐
相关产品推荐

