在SimPy Python中请求第一个可用资源

4 投票
2 回答
2007 浏览
提问于 2025-04-18 15:24

我想创建一个模拟模型,模拟一个有三个柜台的银行。我希望有一个顾客排队的队伍,如果有任何一个柜台空闲,就会为顾客服务一段时间。每个柜台都是独立的资源(我不能把它们当成一个容量为3的资源),而且我需要知道是哪个柜台在为顾客服务。我在实现这个过程中遇到了一些困难。

我找到了一篇文章,似乎也在讨论类似的问题,但我还是不太清楚具体该怎么实现,文章链接在这里:http://comments.gmane.org/gmane.comp.python.simpy.user/1754

我猜我需要在里面用到 yield simpy.AnyOf(env, list_of_resources) 来请求第一个可用的柜台,但我不知道怎么正确设置这个。如果同时有多个资源变得可用,我还想有办法检查一下,这样我就可以选择哪个柜台更适合顾客。

我正在使用 Python 2.7 版本,SimPy 3.0.4 版本。

编辑:添加了代码,展示我想做的事情以及现在为什么不工作。

import simpy
import random

LENGTH_SIM = 200.0

class Customer(object):
    def __init__(self, arrive_time, num):
        self.arrive_time = arrive_time
        self.start_service_time = -1.0
        self.finish_service_time = -1.0
        self.served_by = -1.0
        self.num = num
    def print_attributes(self):
        format_string = "#%3d  Arrive: %6.1f  Start Service: %6.1f  "\
        + " End Service: %6.1f" + "   Served By: %3d"
        print format_string %(self.num, self.arrive_time,
                              self.start_service_time,
                                self.finish_service_time, self.served_by)

class Counter(object):
    def __init__(self, number):
        self.number = number
        self.name = "Counter " + str(number)
        self.start_times = []
        self.end_times = []

class Bank(object):
    def __init__(self, env, num_counters):
        self.counters = [Counter(x+1) for x in range(num_counters)]
        self.num_counters = num_counters
        self.counters_resources = [simpy.Resource(env, capacity=1)
                                   for x in range(num_counters)]


def generate_customers(env, customers):
    count = 1
    while 1:
        wait_time = random.randint(8, 12)
        yield env.timeout(wait_time)
        customers.append(Customer(env.now, count))
        print "Generated Customer: ", count
        count += 1

def select_counter(env, bank):
    '''choose a counter for the customer'''
    for x in range(len(bank.counters)):
        if bank.counters_resources[len(bank.counters) - 1 - x].count == 0:
            print "Counter Selected: ", len(bank.counters) - 1 - x
            print bank.counters[len(bank.counters) - 1 - x].number
            print bank.counters_resources[len(bank.counters) - 1 - x].users
            return len(bank.counters) - 1 - x
    return -2.0

def gen_service_time():
    return random.random() * 20.0 + 21.0

def handle_customer(env, bank, customer, customers, num_process):
    '''handles customer'''
    print "Process ", num_process, " started at:", env.now
    counter = select_counter(env, bank)
    print "Process ", num_process, " after counter select:", env.now
    if counter != -2.0:
        with bank.counters_resources[counter].request() as req:
            yield req
            print "Process ", num_process, "Time", env.now, "In if"
            bank.counters[counter].start_times.append(env.now)
            service_time = gen_service_time()
            customer.start_service_time = env.now
            customer.finish_service_time = env.now + service_time
            bank.counters[counter].end_times.append(env.now + service_time)
            customer.served_by = counter + 1
            yield env.timeout(service_time)
    else:
        reqs = []
        for x in range(len(bank.counters)):
            reqs.append(bank.counters_resources[x].request())
        counter_used = yield simpy.events.AnyOf(env, reqs)
        for x in range(len(reqs)):
            if counter_used.keys()[0] != reqs[x]:
                print "True"
                bank.counters_resources[x].release(reqs[x])
        print "Process ", num_process, "Time", env.now, "In else"
        bank.counters[0].start_times.append(env.now)
        service_time = gen_service_time()
        customer.start_service_time = env.now
        customer.finish_service_time = env.now + service_time
        bank.counters[0].end_times.append(env.now + service_time)
        customer.served_by = 1
        yield env.timeout(service_time)
        for x in range(len(reqs)):
##            if counter_used.keys()[0] == reqs[x]:
            if 1:
                bank.counters_resources[x].release(reqs[x])




def run_bank(env, bank, customers):
    while 1:
        min_time = -1.0
        min_customer = -1.0
        for x in xrange(len(customers)):
            if customers[x].arrive_time < min_time or min_time == -1.0:
                if customers[x].arrive_time > env.now:
                    min_time = customers[x].arrive_time
                    min_customer = x
                else:
                    continue
        if min_time == -1.0:
            yield env.timeout(LENGTH_SIM - env.now)
        yield env.timeout(min_time - env.now)
        print "Calling Process: ", min_customer, "At time:", env.now
        env.process(handle_customer(env,bank,customers[min_customer],customers,
                                    min_customer + 1))

def run_sim():
    env = simpy.Environment()
    customers = []
    env.process(generate_customers(env, customers))
    env.run(until=LENGTH_SIM)
    env2 = simpy.Environment()
    bank = Bank(env2, 3)
    env2.process(run_bank(env2, bank, customers))
    env2.run(until = LENGTH_SIM)
    for x in range(len(customers)):
        customers[x].print_attributes()

if __name__ == '__main__':
    run_sim()

2 个回答

2

如果你真的不能使用一个正常的容量为3的资源,也许可以试试Store。Store是用来存放计数器对象的,你可以向它请求这些计数器。每个计数器都有自己的身份,可以用来跟踪它的使用次数。顾客用完计数器后,需要把它放回Store里:

import simpy


class Counter:
    usages = 0


def customer(env, counters):
    counter = yield counters.get()
    yield env.timeout(1)
    counter.usages += 1
    yield counters.put(counter)


env = simpy.Environment()
counters = simpy.Store(env, capacity=3)
counters.items = [Counter() for i in range(counters.capacity)]
for i in range(10):
    env.process(customer(env, counters))
env.run()
for counter in counters.items:
    print(counter.usages)
1

我自己解决了这个问题,唯一不明白的是取消方法中的不同参数具体有什么作用(我把它们都设置为None,结果也能正常工作,但我不太清楚除了名字之外,还有哪里有关于它们的说明)。修正后的代码如下(相关部分是handle_customer函数中的else语句):

import simpy
import random

LENGTH_SIM = 100.0

class Customer(object):
    def __init__(self, arrive_time, num):
        self.arrive_time = arrive_time
        self.start_service_time = -1.0
        self.finish_service_time = -1.0
        self.served_by = -1.0
        self.num = num
    def print_attributes(self):
        format_string = "#%3d  Arrive: %6.1f  Start Service: %6.1f  "\
        + " End Service: %6.1f" + "   Served By: %3d"
        print format_string %(self.num, self.arrive_time,
                              self.start_service_time,
                                self.finish_service_time, self.served_by)

class Counter(object):
    def __init__(self, number):
        self.number = number
        self.name = "Counter " + str(number)
        self.start_times = []
        self.end_times = []

class Bank(object):
    def __init__(self, env, num_counters):
        self.counters = [Counter(x+1) for x in range(num_counters)]
        self.num_counters = num_counters
        self.counters_resources = [simpy.Resource(env, capacity=1)
                                   for x in range(num_counters)]


def generate_customers(env, customers):
    count = 1
    while 1:
##        wait_time = random.randint(8, 12)
        wait_time = 2
        yield env.timeout(wait_time)
        customers.append(Customer(env.now, count))
        print "Generated Customer: ", count
        count += 1

def select_counter(env, bank):
    '''choose a counter for the customer'''
    for x in range(len(bank.counters)):
        if bank.counters_resources[len(bank.counters) - 1 - x].count == 0 and\
        bank.counters_resources[len(bank.counters) - 1 - x].queue == []:
            print "Counter Selected: ", len(bank.counters) - 1 - x
            return len(bank.counters) - 1 - x
    return -2.0

def gen_service_time():
##    return random.random() * 20.0 + 19.0
    return random.randint(4, 8)

def wait_til_available(env, bank, temp_list):
    while 1:
        yield env.timeout(0.0001)
        for x in range(len(bank.counters)):
            if bank.counters_resources[x].count == 0:
                req = bank.counters_resources[x].request()
                temp_list[0] = x
                temp_list[1] = req
                return 

def handle_customer(env, bank, customer, customers, num_process):
    '''handles customer'''
    print "Process ", num_process, " started at:", env.now
    counter = select_counter(env, bank)
    if counter != -2.0:
        with bank.counters_resources[counter].request() as req:
            yield req
            bank.counters[counter].start_times.append(env.now)
            service_time = gen_service_time()
            customer.start_service_time = env.now
            customer.finish_service_time = env.now + service_time
            bank.counters[counter].end_times.append(env.now + service_time)
            customer.served_by = counter + 1
            yield env.timeout(service_time)
    else:
        reqs = []
        got_counter = 0
        for x in range(len(bank.counters)):
            reqs.append(bank.counters_resources[x].request())
        good_req = yield simpy.events.AnyOf(env, reqs)
        req = good_req.keys()[0]
        for x in range(len(bank.counters)):
            if req != reqs[x]:
                bank.counters_resources[x].release(reqs[x])
                reqs[x].cancel(None, None, None)
            else:
                got_counter = x
        bank.counters[got_counter].start_times.append(env.now)
        service_time = gen_service_time()
        customer.start_service_time = env.now
        customer.finish_service_time = env.now + service_time
        bank.counters[got_counter].end_times.append(env.now + service_time)
        customer.served_by = got_counter + 1
        yield env.timeout(service_time)
        print "Process ", num_process, " finished at:", env.now
        bank.counters_resources[got_counter].release(req)




def run_bank(env, bank, customers):
    while 1:
        min_time = -1.0
        min_customer = -1.0
        for x in xrange(len(customers)):
            if customers[x].arrive_time < min_time or min_time == -1.0:
                if customers[x].arrive_time > env.now:
                    min_time = customers[x].arrive_time
                    min_customer = x
                else:
                    continue
        if min_time == -1.0:
            yield env.timeout(LENGTH_SIM - env.now)
        yield env.timeout(min_time - env.now)
        print "Calling Process: ", min_customer+1, "At time:", env.now
        env.process(handle_customer(env,bank,customers[min_customer],customers,
                                    min_customer + 1))

def run_sim():
    env = simpy.Environment()
    customers = []
    env.process(generate_customers(env, customers))
    env.run(until=LENGTH_SIM)
    env2 = simpy.Environment()
    bank = Bank(env2, 3)
    env2.process(run_bank(env2, bank, customers))
    env2.run(until = LENGTH_SIM)
    for x in range(len(customers)):
        customers[x].print_attributes()

if __name__ == '__main__':
    run_sim()

撰写回答