队列和中断的Simpy模拟

Simpy Simulation of queue and interruption

我正在尝试使用 Simpy 的 PreemptiveResource 来模拟一个队列。服务器重复以下循环。在每个周期中,服务器运行 0.09 个单位的时间,服务器将关闭 0.01 个单位的时间。如果服务器关闭时服务器当前正在为客户服务,则客户将立即离开。服务恢复后,将为下一位排队的顾客提供服务。但是我的输出好像是客户打断后不会离开。有人可以解释如何解决这个问题吗?非常感谢。

import numpy as np
import simpy

def arrival(lmbda):
i=0
while True:
    inter_arrival=-1/lmbda*np.log(np.random.rand())
    yield env.timeout(inter_arrival)
    i+=1
s1=env.process(service(i))
env.process(shutdown(i,s1))
print(i,"arrival", env.now)

def shutdown(i,s1):
while True:

    #########the server is functional for 0.09 units of time

    yield env.timeout(0.09)

    #########if customer still in the queue

    if rqt_list[i-1].processed==False:
        s1.interrupt()   
    else:
        return

def service(i ):

    ###########requesting the server

    rqt=desk.request()
    rqt_list.append(rqt)
    print(i, "request", env.now)

    while True:
        try:
            yield rqt
            break
        except simpy.Interrupt:

            #########leave [delete request from the queue]

            rqt.cancel()
            print(i, "server shuts down", env.now)

            #########the server will shut down for 0.01 units of time

            yield env.timeout(0.01)

            #return, generate a new request

            rqt=desk.request()
            rqt_list[i-1]=rqt
    print(i,  "start the service", env.now)

    yield env.timeout(0.2)
    print(i, "end the service", env.now)
    desk.release(rqt)

env=simpy.Environment()
env.process(arrival(lmbda=7))
rqt_list=[]
desk=simpy.PreemptiveResource(env)
T=1
env.run(until=T)

我没有使用中断。相反,当服务器状态变为非活动状态时,我使用了一个事件来发出信号。然后服务延迟产生于服务时间超时或服务器状态更改事件,以先到者为准。我认为这使客户更清洁客户不需要任何尝试/除外。如果服务因服务器不活动而中断,如果客户需要执行特殊操作,客户仍然可以检查服务器的状态。

代码如下:

"""
example of a server with breaks

programmer: Michael R. Gibbs
"""

import simpy
import numpy as np 

class Server():
    """
    Provides service to customers, but the service ends if the server goes on break
    """

    def __init__(self, env, serverId):
        self.env = env
        self.serverId = serverId
        self.state = "Active"               # is ther server Active or Inactive
        self.stateChange = self.env.event() # event to yeild on for when server state changes (go on/off break)

        self.activeTime = 0.9
        self.inactiveTime = 0.1

        self.serviceTime = 0.2

        # start the server
        env.process(self.lifeLoop())

    def lifeLoop(self):
        """
        Manages that state of the server (Active or Inactive)
        also uses a event to braodcast when the state changes
        """

        while True:
            # active state
            yield env.timeout(self.activeTime)
            print(self.env.now, f"server {self.serverId} is becoming inactive")
            self.state = "Inactive"

            # use event to braodcast state has change
            oldEvent = self.stateChange
            self.stateChange = self.env.event()
            oldEvent.succeed()

            # inactive state
            yield env.timeout(0.1)
            print(self.env.now, f"server {self.serverId} is becoming active")
            self.state = "Active"

            # use event to bradcast state has changed
            oldEvent = self.stateChange
            self.stateChange = self.env.event()
            oldEvent.succeed()

    def service(self):
        """
        The service delay
        ends when service time is up, or if the server becomes inactive
        """
        yield env.any_of([self.stateChange, env.timeout(self.serviceTime)])


class ServiceDesk():
    """
    Manages the queue for getting a server
    """

    def __init__(self, env, serverCnt=2):
        self.env = env
        self.serverQ = simpy.Store(env, capacity=serverCnt)

        # create the servers and add to the queue
        for i in range(serverCnt):
            server = Server(env,i+1)
            self.serverQ.put(server)

    def getServer(self):
        """
        Gets a server
        
        Servers can become inactive waitting in the queue
        only return "Active" servers
        """

        server = None

        # keep searching the queue until a "Active" server is found
        while server is None:
            server = yield self.serverQ.get()
            
            if server.state == "Active":
                return server

            else:
                # to prevent a infinate loop, cache inative server in a break process
                env.process(self.serverBreak(server))
                server = None

    def serverBreak(self, server):
        """
        Wait for server to become "Active" before putting in back into the queue
        """

        yield server.stateChange
        self.freeServer(server)

    def freeServer(self, server):
        """
        puts the server back into the queue
        """
        self.serverQ.put(server)

def customer(env, customerId, serviceDesk):
    """
    Customer arrives
    gets server
    gets service from server
    returns server
    leaves
    """

    # arrives
    print(env.now, f"customer {customerId} has arrived")

    # gets server
    server = yield env.process(serviceDesk.getServer())
    print(env.now, f"customer {customerId} got server {server.serverId}")

    # gets service
    yield env.process(server.service())
    print(env.now, f"customer {customerId} server {server.serverId} finished service")

    # return server
    serviceDesk.freeServer(server)

    # leaves
    print(env.now, f"customer {customerId} has left")

def genCustomers(env, lmbda, serviceDest):
    """
    generates the arrival of customers
    """

    i=0 # customer id

    while True:
        inter_arrival=-1/lmbda*np.log(np.random.rand())
        yield env.timeout(inter_arrival)
        i+=1
        env.process(customer(env, i, serviceDesk))

# start the simulation
env=simpy.Environment()
serviceDesk = ServiceDesk(env)
env.process(genCustomers(env,7,serviceDesk))

env.run(5)