如何中断或取消 SimPy 超时事件?

How do I interrupt or cancel a SimPy Timeout event?

我想创建一个带有回调的计时器,该回调可以使用 SimPy 中断或重置。如果被打断,我不希望执行回调,如果重置,我希望计时器从 env.now 开始以相同的延迟重新启动。这看起来很简单,最初只需使用 env.timeout 就可以做到。但是,文档说明:

To actually let time pass in a simulation, there is the timeout event. A timeout has two parameters: a delay and an optional value: Timeout(delay, value=None). It triggers itself during its creation and schedules itself at now + delay. Thus, the succeed() and fail() methods cannot be called again and you have to pass the event value to it when you create the timeout.

因为模拟starts触发了,我不能加回调因为你不能调用fail,我不能打断超时

我考虑过只实现一个等待一个时间步长的进程,如果它被中断或到达它正在等待的 env.now,则检查一个标志,但这似乎效率低得可怕,如果我有很多定时器(我会的),我担心发电机的数量会压倒模拟。 (超时功能似乎通过在模拟的未来安排自己来工作,这就是为什么你可以拥有大量 运行 的原因)。

所以规范是 - 创建一个在指定时间后触发回调的事件,但可以在该时间发生之前重置或中断。有什么想法吗?

好吧,如果我正确理解了你的问题,你可以做的一件事是创建一个 Timer class 和一个检查 simpy.Interrupt 的等待方法。您可以实现 stop(),这样当它被调用时,您也可以调用 interrupt()。这样,只要先前调用了 interrupt(),就不会执行回调。重置方法将简单地再次调用 stop()(中断)和 start(),从而将操作设置回 running() 并再次调用 wait(),允许回调在每次执行后再次执行超时直到再次调用中断。

下面是这样一个 Timer class 的示例实现:

import simpy

class Timer(object):

    def __init__(self, env, delay, callback):
        self.env      = env 
        self.delay    = delay
        self.action   = None
        self.callback = callback
        self.running  = False
        self.canceled = False

    def wait(self):
        """
        Calls a callback after time has elapsed. 
        """
        try:
            yield self.env.timeout(self.delay)
            self.callback()
            self.running  = False
        except simpy.Interrupt as i:
            print "Interrupted!"
            self.canceled = True
            self.running  = False

    def start(self):
        """
        Starts the timer 
        """
        if not self.running:
            self.running = True
            self.action  = self.env.process(self.wait())

    def stop(self):
        """
        Stops the timer 
        """
        if self.running:
            self.action.interrupt()
            self.action = None

    def reset(self):
        """
        Interrupts the current timer and restarts. 
        """
        self.stop()
        self.start()

我知道这是一个古老的 SO 问题,但我一直在寻找一个简单的 SimPy Timer class 就像 (which was very useful!), and had a fix for the issue with reset pointed out by @bbengfort. The reset function assumes that interrupt executes immediately, but this is not the case as explained in the SimPy documentation:

中提供的一样

What process.interrupt() actually does is scheduling an Interruption event for immediate execution.

所以对 self.start 的调用实际上是在 wait 进程被中断之前执行的,这阻止了它启动一个新的 wait 进程。

下面是修改后的版本,重置应该可以正常工作。

import simpy

class Timer(object):

    def __init__(self, env, delay, callback):
        self.env      = env 
        self.delay    = delay
        self.action   = None
        self.callback = callback

    def wait(self):
        """
        Calls a callback after time has elapsed. 
        """
        try:
            yield self.env.timeout(self.delay)
            self.callback()
        except simpy.Interrupt:
            print("Interrupted!")

    def running(self):
        """
        Check if timer is running
        """
        return self.action is not None and self.action.is_alive

    def start(self):
        """
        Starts the timer 
        """
        if not self.running():
            self.action  = self.env.process(self.wait())

    def stop(self):
        """
        Stops the timer 
        """
        if self.running():
            self.action.interrupt()
            self.action = None

    def reset(self):
        """
        Interrupts the current timer and restarts. 
        """
        self.stop()
        self.start()