【问题标题】:Simulating a system of resource with set-up/switch-on times using Simpy使用 Simpy 模拟具有设置/开启时间的资源系统
【发布时间】:2025-11-25 13:05:02
【问题描述】:

我想创建一个带有服务器的系统,该系统需要时间进行设置才能准备好服务。由于服务器的数量会随着时间而变化,我认为 Container 资源可能会起作用。每当有客户到达队列时就会设置服务器,并且较早到达的客户将抢占较早开启的服务器,如下所示。

  1. 客户 1 到达并请求服务器。
  2. 服务器 1 在 t1 秒内设置完毕。
  3. 客户 2 到达并请求服务器。
  4. 服务器 2 在 t2 秒内设置完毕。
  5. 服务器 2 已开启。
  6. 客户 1 占用服务器 2。

我想知道如何才能使上述过程真正起作用。如果我按如下方式安排事件,则每个循环似乎都在yield req 之后被卡住(yield req 不会将此请求放入队列并在稍后在yield servers.server.put(1) 打开准备好的服务器后立即完成请求?)

with servers.computer.get(1) as req: 
      yield req                       
      yield env.timeout(switch_on())    #switch on time
      yield servers.server.put(1) 

"""0.09948 Job0 arrives
0.25648 Job1 arrives
0.37188 Job2 arrives
0.47028 Job3 arrives
0.53916 Job4 arrives
0.66893 Job5 arrives
"""

如果我按照下面的顺序更改顺序,那么无论服务器的顺序如何,客户-i都肯定会占用服务器-i。

with servers.computer.get(1) as req: 
      yield env.timeout(switch_on())    #switch on time
      yield servers.server.put(1)
      yield req 

这是完整的代码。

import simpy

LAM = 8  #arival rate of jobs
MU = 2  #service rate
ALPHA = 12  #set up rate
NUM_SERVERS = 5
MAX_NUM_JOB = 10000000000
UNTIL = 10

def generate_interarrival():
  return np.random.exponential(1/LAM)

def generate_service():
  return np.random.exponential(1/MU)

def switch_on():
  return np.random.exponential(1/ALPHA)

class Generate_Job(): 
    def arriving_job(env, servers):
        global  num_current_jobs, num_server_on, leaving_time_list
        for i in range(MAX_NUM_JOB):
            job = Job(name="Job%01d" % (i))
            yield env.timeout(generate_interarrival())
            print('{0:.5f}'.format(env.now), job.name, "arrives")

            env.process(job.handling(env,servers))

class Room:                             # A room containing servers (resource)
    def __init__(self, env):
        self.computer = simpy.Container(env, capacity = 10000, init = 0)


class Job(object):
    def __init__(self,name):
        self.name = name

    def handling(self, env, servers):
        with servers.computer.get(1) as req: 
            yield req                       
            yield env.timeout(switch_on())    #switch on time
            yield servers.server.put(1)            
            print('{0:.5f}'.format(env.now), self.name, "occupies a server--")        
            yield env.timeout(generate_service())    #service time
            print('{0:.5f}'.format(env.now), self.name, "leaves")


np.random.seed(0)
env = simpy.Environment()
servers  = Room(env)
env.process(Generate_Job.arriving_job(env, servers))
env.run(until = UNTIL) 


【问题讨论】:

    标签: simulation simpy


    【解决方案1】:

    需要改变两件事

    您的资源池一开始是空的,这意味着您对资源的第一次请求将永远等待并且永远不会访问您添加资源的代码。

    我在请求之前添加了一个检查,如果需要,它将添加资源。由于添加资源存在延迟,因此资源请求在资源“启动”时仍然会等待。不要使用 yield 调用此检查,否则请求将等待添加新资源,如果由于另一个添加而添加了资源,或者另一个作业完成并在添加新资源之前返回资源,则请求将丢失

    此外,容器不会在“with”语句的末尾返回资源,只有资源会这样做。

    这是固定的代码,让我知道你的想法

    """
    Simulation of a dynamic server pool
    
    Server pool starts empty and servers are added as needed, but there is a delay 
    simulating start up time before the server is available to fulfill a resource request
    
    Programmer: Matt
        Wrote original version
    
    Programmer: Michael R. Gibbs
        Added server check for dynamicaly adding servers
        Fixed return of resorces to pool
    """
    
    import simpy
    import numpy as np
    
    LAM = 8  #arival rate of jobs
    MU = 2  #service rate
    ALPHA = 12  #set up rate
    NUM_SERVERS = 5
    MAX_NUM_JOB = 10000000000
    UNTIL = 10
    
    def generate_interarrival():
      return np.random.exponential(1/LAM)
    
    def generate_service():
      return np.random.exponential(1/MU)
    
    def switch_on():
      return np.random.exponential(1/ALPHA)
    
    def check_servers(env, servers):
        """
        Checks the server pool to see if the pool has any avalable servers
        if not then add a server, (there will be a delay before added server becomes available)
    
        Call this without a yield so it does not block if a server is added
        """
    
        print('{0:.5f}'.format(env.now), "checking server pool", "requests:",len(servers.get_queue), "servers:", servers.level)
        
        if len(servers.get_queue) >= servers.level:
            # will need another server
            d = switch_on()
            print('{0:.5f}'.format(env.now), "adding a server at " + '{0:.5f}'.format(env.now + d) + " --")
            yield env.timeout(d)    #switch on time
            yield servers.put(1)      
            print('{0:.5f}'.format(env.now), "added a server--")
    
    class Generate_Job(): 
        def arriving_job(env, servers):
            global  num_current_jobs, num_server_on, leaving_time_list
            for i in range(MAX_NUM_JOB):
                job = Job(name="Job%01d" % (i))
                yield env.timeout(generate_interarrival())
                print('{0:.5f}'.format(env.now), job.name, "arrives")
    
                env.process(job.handling(env,servers))
    
    class Room:                             # A room containing servers (resource)
        def __init__(self, env):
            self.computer = simpy.Container(env, capacity = 10000, init = 0)
    
    
    class Job(object):
        def __init__(self,name):
            self.name = name
    
        def handling(self, env, servers):
    
            # added a check to see if a resource pool needs another server.
            env.process(check_servers(env,servers.computer))
            print('{0:.5f}'.format(env.now), self.name, "requesting a server--") 
    
            with servers.computer.get(1) as req: 
                
                yield req 
                # if the queue is empty then the req is never filled and the next lines are never called
                # need to do this before the rescource requests
                #                       
                # yield env.timeout(switch_on())    #switch on time
                # yield servers.server.put(1)            
                print('{0:.5f}'.format(env.now), self.name, "occupies a server--")        
                yield env.timeout(generate_service())    #service time
                print('{0:.5f}'.format(env.now), self.name, "leaves")
    
                # containers do not return a resouce at the end of a "with"
                # added a put
                yield servers.computer.put(1)
    
    
    np.random.seed(0)
    env = simpy.Environment()
    servers  = Room(env)
    env.process(Generate_Job.arriving_job(env, servers))
    env.run(until = UNTIL) 
    

    【讨论】:

    • 作为一项增强功能,您可以扩展资源池检查,以便在池变得太大而无法模拟多余服务器的停机时也发出自己的资源请求
    • 非常感谢!这就像一个魅力!所以关键是添加一个与客户服务流程并行运行的“服务器管理”流程,不是吗?现在,假设还有一个策略,例如,如果客户使用完计算机并看到队列不为空,他将保持计算机开启,以便等待的人可以立即进入(yield servers.computer.put(1)),否则他立即关闭计算机。在这种情况下,我是否应该创建一个“Server_Management”类,其中包含两种方法,例如 check_servers_arriving 和 check_servers_leaving - 这有意义吗?
    • 是的,我想你明白了。您可以使用资源池的级别属性来确定您是实际将资源返回到池中,还是将其关闭。您能将此答案标记为正确吗?
    • 非常感谢,迈克尔。您的回答完全解决了本主题中提出的问题。但是,出现了另一个问题,我发布了另一个关于它的问题。请问您也可以看看吗? *.com/questions/67757880/…