Simpy离散事件仿真(8)——专题指南(4)——进程交互

1 进程交互

离散事件仿真只有通过进程之间的相互作用才能引起人们的兴趣。

所以章节指南是关于:

(1)休眠直到唤醒(被动/重新激活)

(2)等待另一个进程终止

(3)中断另一个进程

前两项已经在事件指南中介绍过了,但是为了完整起见,我们还将在这里介绍它们。

进程交互的另一种可能场景是资源的使用,将在单独的指南中讨论。

 

2 休眠直到唤醒

想象一下您给一辆带有智能充电控制器的电动汽车建模。当车辆行驶时,控制器是被动的;在车辆连接到电网后重新激活,以便为蓄电池充电。

在SimPy 2中,这种模式被称为被动/重新激活。在SimPy 3中,您可以通过一个简单的共享事件来实现这一点:

from random import seed, randint
seed(23)

import simpy

class EV:
    def __init__(self, env):
        self.env = env
        self.drive_proc = env.process(self.drive(env))
        self.bat_ctrl_proc = env.process(self.bat_ctrl(env))
        self.bat_ctrl_reactivate = env.event()

    def drive(self, env):
        while True:
            # 行驶20-40分钟
            yield env.timeout(randint(20, 40))

            # 停车1–6小时
            print('开始停车的时间:', env.now)
            self.bat_ctrl_reactivate.succeed()  # "激活"
            self.bat_ctrl_reactivate = env.event()
            yield env.timeout(randint(60, 360))
            print('结束停车时间:', env.now)

    def bat_ctrl(self, env):
        while True:
            print('控制器被动模式时间:', env.now)
            yield self.bat_ctrl_reactivate  # "passivate"
            print('控制器激活时间:', env.now)
            # 智能充电 …
            yield env.timeout(randint(30, 90))

env = simpy.Environment()
ev = EV(env)
env.run(until=150)

 

由于bat_ctrl()只等待一个常规事件,因此我们在SimPy 3中不再调用被动/重新激活。

 

3 等待另一个进程终止

上面的示例有一个问题:车辆可能希望停车的时间比充电电池所需的时间短(如果充电和停车都需要60到90分钟,则会出现这种情况)。

要解决这个问题,我们必须稍微调整我们的模型。每次电动汽车开始停车时,都会启动一个新的bat_ctrl()。然后,电动汽车等待停车时间结束和充电停止:

from random import seed, randint

import simpy
seed(23)

class EV:
    def __init__(self, env):
        self.env = env
        self.drive_proc = env.process(self.drive(env))


    def drive(self, env):
        while True:
            # 行驶20-40分钟
            yield env.timeout(randint(20, 40))

            # 停车1–6小时
            print('开始停车时间', env.now)
            charging = env.process(self.bat_ctrl(env))
            parking = env.timeout(randint(60, 360))

            yield charging & parking
            print('结束停车时间:', env.now)
            
              

    def bat_ctrl(self, env):
        print('控制器启动时间:', env.now)
        # 智能充电 …
        yield env.timeout(randint(30, 90))
        print('控制器完成时间:', env.now)

env = simpy.Environment()
ev = EV(env)
env.run(until=310)
开始停车时间 29
控制器启动时间: 29
控制器完成时间: 60
结束停车时间: 131
开始停车时间 169
控制器启动时间: 169
控制器完成时间: 226

同样,没有什么新的和特别的事情发生(如果你读过Events指南)。SimPy进程也是事件,因此您可以生成它们并等待它们被触发。您还可以通过将两个事件用&连接(请参见一次等待多个事件),同时等待两个事件。

 

4 中断另一个进程

像往常一样,我们现在还有另一个问题:想象一下,一次旅行是非常紧急的,但是在目前的实施中,我们总是需要等到电池充满电。如果我们能打断…

幸运的是,确实有办法做到这一点。可以对进程调用interrupt()。这将在该进程中引发中断异常,并立即恢复:

from random import seed, randint

import simpy
seed(23)

class EV:
    def __init__(self, env):
        self.env = env
        self.drive_proc = env.process(self.drive(env))


    def drive(self, env):
        while True:
            # 行驶20-40分钟
            yield env.timeout(randint(20, 40))

            # 停车1小时
            print('开始停车时间', env.now)
            charging = env.process(self.bat_ctrl(env))
            parking = env.timeout(60)

            yield charging | parking
            if not charging.triggered:
                # 中断充电
                charging.interrupt('该出发了!')
            print('结束停车时间:', env.now)
            
              
    def bat_ctrl(self, env):
        print('充电器启动时间:', env.now)
        try:
            yield env.timeout(randint(60, 90))
            print('充电器完成时间:', env.now)
        except simpy.Interrupt as i:
            # 充电被中断
            print('充电器中断时间:', env.now, 'msg:', i.cause)        
        
env = simpy.Environment()
ev = EV(env)
env.run(until=100)
开始停车时间 29
充电器启动时间: 29
结束停车时间: 89
充电器中断时间: 89 msg: 该出发了!

process.interrupt()实际上做的是调度一个中断事件以便立即执行。如果执行此事件,它将从当前正在等待的事件的回调中删除受影响进程的_resume()方法(请参阅target)。然后它将把中断异常抛出到进程中。

由于我们没有对进程的原始目标事件做任何特殊的操作,中断的进程在捕捉到中断后可以再次产生相同的事件——想象有人在等待商店开门,这个人可能会被电话打断。打完电话后,他(她)会检查商店是否已经开门,是否进入或继续等待。

你可能感兴趣的:(学习教程,离散事件仿真,python,仿真器)