如企业网站模板下载,wordpress响应时间长,专题网站怎么做,秦皇岛网站定制哪家好文章目录 内容介绍详细执行逻辑分析大致仿真流程Simpy核心类的细节Environment 类Event 类Process类#xff08;Event#xff09; 基于案例详细介绍仿真逻辑env.run() 方法逻辑env.process() 方法逻辑 参考文章#xff1a;
SimPy Discrete event simulation for Pythonpyth… 文章目录 内容介绍详细执行逻辑分析大致仿真流程Simpy核心类的细节Environment 类Event 类Process类Event 基于案例详细介绍仿真逻辑env.run() 方法逻辑env.process() 方法逻辑 参考文章
SimPy Discrete event simulation for Pythonpython离散事件仿真库SimPy官方教程离散事件仿真原理DES
内容介绍
离散事件仿真库Simpy的执行效率之所以很高关键在于生成器的使用在Python中通过yield来暂时停止协程中的子线程再次调用时才从中断的位置开始。前面的文章《关于复制SimPy仿真环境的生成器的讨论》中我们介绍了生成器的相关特性。
本文基于简单的例子介绍Simpy仿真环境的事件流过程。是如何控制调用生成器抛出的事件以及如何控制仿真过程的结束。以下是一个公开找到的simpy仿真案例
import simpydef main():env simpy.Environment()env.process(traffic_light(env))env.run(until140)print(simulation done)def traffic_light(env):while True:print(flight green at :{env.now})yield env.timeout(30)print(flight yellow at :{env.now})yield env.timeout(5)print(flight red at :{env.now})yield env.timeout(20)if __name__ __main__:main()其中先通过simpy创建了一个仿真环境在环境中定义了一个 process方法并将我们自定义的 traffic_light 作为参数传入该方法然后运行之后会不断地在仿真环境 env 调用 traffic_light 方法。返回结果如下
light green at :0
light yellow at :30
light red at :35
light green at :55
light yellow at :85
light red at :90
light green at :110
simulation done详细执行逻辑分析
大致仿真流程
上述案例的运行逻辑是先通过 simpy.Environment() 创建仿真环境 env然后在环境中添加一个事件 process(traffic_light(env))事件调用了一个函数 traffic_light(env)该函数的 env 为指明该函数的仿真环境函数下的事件 env.timeout(30) 推进的是 env 的仿真事件。
在调用函数 traffic_light 时不断地打印输出相应内容以及推进仿真事件当仿真事件被推进到终止的时间点时 env.run(until140)仿真结束在还未到达结束事件之前事件会基于上次中断的位置一直抛出事件并推进仿真。
Simpy核心类的细节
Environment 类
创建的仿真环境 env 的内容这是基于事件的模拟执行环境随着仿真事件的步进一步步地从一个事件推进到另一个事件来进行模拟。simpy.Environment() 可以传入仿真环境的初始时间申明传入的值可以为 int 类型或 float 类型若为空则默认初始时间为 0。并且在该环境中将常见的事件命名为三类对象processtimeoutevent。
依次介绍 Event 类的属性和方法
_now仿真时间_queue当前待处理的事件一个列表元素为四元元组元组的信息包含一个数值、事件优先级、事件ID、事件对象_eid迭代器生成事件ID每次有事件安排进 _queue 时都会赋予被安排事件一个iD_active_proc 存储当前正在执行的 Process 事件BoundClass.bind_early(self) 用于将 BoundClass 类型的属性绑定到环境实例上以优化属性搜索时的开销但会引入复杂性或影响代码的可维护性(property) now()返回环境的仿真时间被 property 修饰为一个只读属性可以直接视为一个属性进行访问例如 env.now 无需加括号即可返回环境类的当前仿真时间active_process() 返回环境类当前执行中的 process 对象schedule()基于事件给定的优先级和时间nowdelay通过 heqppush 将事件四元元组插入到环境的事件队列 _queue 中按照排程时间、优先级从小到大依次有序地从堆顶存放到堆底peek()函数返回时间队列下一个待处理事件的排程时间若没有顺位的事件则返回 Infinity无穷大的浮点数step() 函数处理下一个事件从 _queue 的堆顶弹出下一个处理事件最早的事件如果没有下一个事件则抛出 EmptySchedule 错误run()环境类的入口函数通过该函数让整个仿真环境运行起来其中参数 until 可以传入整数、浮点数以及事件对象。如果没有传入值则仿真环境会运行至没有需要加工的事件如果传入的是一个事件则仿真环境会持续执行加工直到该事件被触发如果在该事件被触发之前已经没有待处理事件了则弹出 RuntimeError 错误如果是一个数值则仿真环境会持续执行直到仿真时间到达该数值具体操作为创建一个在 nowuntil 触发的截止事件该事件的优先级为0当运行到该事件时则触发 StopSimulation结束仿真。 因此结束仿真事件的优先级为0因此如上面的交通灯的案例如果最后一个时间的触发的时间为140且仿真环境的until140则最后一个事件不会被处理仿真优先被终止。 Event 类
这是仿真中用来定义事件的类每个事件都会在某个时间点发生。有三种状态可能发生未被触发triggeredFalse、正在发生被触发triggeredTrue、已经发生processedTrue。每个事件在初始化的时候都处于未被触发状态被触发后会被安排进环境的 _queue 待加工队列当被触发时事件的 ok() 与 value() 会被修改。
依次介绍 Event 类的属性和方法
_ok布尔变量_defused布尔变量_value默认值为 PENDING 对象__init__(env)定义事件所在的 仿真环境初始化事件的 callbacks 为一个空列表__repr__()返回事件的类名、事件id(property) triggered()当事件被触发且它的 callbacks 即将被调用是返回 True也就是 self._value is not PENDING如果没有被触发则 _value 的值还是 PENDING这是一个具体的对象用来唯一标识 _value(property) processed()当事件已经完成它的callbacks已经被调用时返回 True此时 callbacks is None(property) ok()返回 _ok 属性的值当事件被触发时改属性值为 True如果事件在被触发之前被访问则抛出 AttributeError 错误(property) defused()返回对象是否存在 _defused 的判断当一个事件失败后则失败的事件的 value 会变成一个错误类型在被调用该事件时被重新抛出(defused.setter) defused()不论传入何值都将事件的 _defused 赋值为 True(property) value()当事件没有被触发此时 _value 的值还是 PENDING调用该方法会抛出 AttributeError反之则会返回 _value 的值trigger()将事件的 _ok 和 _value 的值更新为传入事件的 _ok 和 _value 的值并将本事件排进环境的 _queuesucceed()触发事件如果 _value is not PENDING则抛出 RuntimeError说明事件在之前已经被触发了反之则修改 _ok 的值为 True并对 _value 进行赋值并将该时间排进环境的 _queuefail()触发事件失败传入一个错误类型如果事件未被触发则将 _ok 赋值为 False并将 _value 赋值为传入的错误类型将该时间放入到环境的 _queue 中__and__返回一个 Condition 类对象只有当前事件与 other 事件都加工完这个 Condition 类对象被触发__or__返回一个 Condition 类对象只有当前事件或 other 事件都加工完这个 Condition 类对象被触发
Process类Event
用来处理生成器产生的事件也被成为协程可以通过产生事件来暂停执行。process 在时间发生后使用该事件的值恢复生成器的执行。对于失败的事件生成器会抛出异常。
Process 本身也是一个事件每当生成器返回或抛出异常它就会被触发它的 value 就是生成器的返回值或接到的抛出的异常情况。process 在执行过程中可以通过方法 interrupt 进行中断。
依次介绍 Process类的属性和方法
env指明 process对象所在的仿真环境callbacks空列表_generator传入的生成器带 yield 的函数_target初始化 Initialize开始 process 的执行_desc()返回 process 的类名以及生成器名(property) target()返回 process 时间的 _target即 process 当前等待执行的事件若 process 被中断则该方法返回 None (property) is_alive()返回 True 直到退出生成器interrupt()提供一个原因并中断该 process 事件如果该 process 事件已经被中断则不能再中断_resume()基于事件的值恢复process事件的执行通过向生成器发送当前事件的值并获取生成器的下一个事件如果生成器退出则 process 事件会基于生成器返回的 value 以及报错信息进行触发。
基于案例详细介绍仿真逻辑
基于上面提到的小案例我们将详细分析这个案例在 simpy 中的底层执行逻辑。再回顾下代码
import simpydef main():env simpy.Environment()env.process(traffic_light(env))env.run(until140)print(simulation done)def traffic_light(env):while True:print(flight green at :{env.now})yield env.timeout(30)print(flight yellow at :{env.now})yield env.timeout(5)print(flight red at :{env.now})yield env.timeout(20)if __name__ __main__:main()根据 main() 函数首先创建了仿真环境 Environment 对象仿真环境的默认起始时间为 0。
env simpy.Environment()接着实例化一个 Process 对象传入的参数为 traffic_light() 生成器其中env 传入该生成器是为了在生成器当中调用仿真环境的相关属性
env.process(traffic_light(env))接着传入 until140 给仿真环境的 env.run() 函数开始执行仿真过程并且将仿真结束时间定为 140。
env.run(until140)env.run() 方法逻辑
具体进入 run()首先传入的 until 不为空此时判断传入的 until 是个事件还是个数值现在传入的是数值将 until 的值赋给局部变量 at如果这个仿真结束时间小于等于仿真环境的当前时间则抛出 ValueError基于 untile 的值创建一个在 untilnow 时候触发的事件且该事件的优先级为最高优先级0并给给这个事件的 callbacks 添加 StopSimulation.callback。
循环地 try 仿真环境的 step() 函数当执行到仿真环境终止的 until 事件抛出的 StopSimulation 错误会被捕获仿真过程会被终止当step() 函数中 heappop(self._queue) 取不出下一个待处理事件则抛出 EmptySchedule 错误捕获该错误时说明全部的事件都被执行结束如果 until 事件还没被触发则会抛出 RuntimeError 错误错误信息如下反之则正常结束仿真。
raise RuntimeError(fNo scheduled events left but until event was not triggered: {until})env.process() 方法逻辑
run() 方法是关于如何把仿真环境当中的 _queue 按顺序执行完而 process() 方法会基于生成器将一个个事件通过 env.schedule(self) 加入到仿真环境的 _queue这里 schedule() 的默认优先级为 NORMAL1默认的 delay0说明生成器的事件是等到要处理时被加入到 _queue。
具体的env.process(traffic_light(env)) 生成了一个 process 事件对象 Process(self, traffic_light(env))在该 process 事件对象中优先初始化了一个 Initialize() 事件并以最高优先级0将该初始化事件加入到仿真环境的 _queue 当中表示 process 事件开始执行。在初始化事件 Initialize().__init()将传入的 Process 对象的 _resume 方法加入到初始化事件的 callbacks 列表当中这个列表在 env.step() 当中会被执行即在处理下一步事件时需要先处理的事件)因此在初始化事件当中就需要先处理 Process 对象的 _resume 方法它不断地从 process 中获取下一个待执行的事件。