Simpy: одновременно освободить ресурс при выделении нового ресурса

Я упростил версию проблемы, которую я пытаюсь смоделировать, где я использую Simpy для описания движения путешественников по пути.

Путь представлен коллекцией Node() объекты, где каждый узел содержит Simpy.Resource, Каждый узел подключен к следующему узлу в пути connected_to приписывать. В примере кода я создал список из 10 узлов, где каждый узел в списке связан с предыдущим узлом в списке.

Когда путешественник (представлен Occupier() объект) создается ресурс узла. Затем путешественник перемещается по узлам, делая шаг только при наличии следующего узла. Моя цель состоит в том, чтобы путешественник одновременно выделил свой конечный узел и освободил узел, где он был ранее расположен.

import simpy


class Node(object):
    def __init__(self, env):
        self.env = env
        self.resource = simpy.Resource(self.env)
        self.up_connection = None
        self.travel_delay = 5


class Occupier(object):
    def __init__(self, env):
        self.env = env
        self.location = None
        self.destination = None
        self.requests = []

    def travel(self, instantiation_loc):
        self.requests.append(instantiation_loc.resource.request())
        yield(self.requests[-1])

        self.location = instantiation_loc
        self.destination = instantiation_loc.up_connection
        yield self.env.timeout(self.location.travel_delay)
        node_occupancy(nodes)

        while self.destination.up_connection != None:
            self.requests.append(self.destination.resource.request())
            yield self.requests[-1]

            self.location.resource.release(self.requests[0])
            self.requests.pop(0)
            self.location = self.destination
            self.destination = self.location.up_connection

            yield self.env.timeout(self.location.travel_delay)
            node_occupancy(nodes)


def node_occupancy(nodes):
    print([node.resource.count for node in nodes])       


env = simpy.Environment()

nodes = [Node(env) for i in range(10)]
for i in range(len(nodes) - 1):
    nodes[i].up_connection = nodes[i + 1]

env.process(Occupier(env).travel(nodes[0]))

env.run()

Если я запускаю приведенный выше код с одним путешественником, он, кажется, работает нормально, давая следующий вывод:

[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0]

Однако, если создать экземпляр второго путешественника, вы увидите, что есть моменты времени, когда один путешественник занимает два ресурса, когда он должен занимать только один:

env.process(Occupier(env).travel(nodes[3]))
env.process(Occupier(env).travel(nodes[0]))

соответствующий вывод:

[1, 0, 0, 1, 0, 0, 0, 0, 0, 0]
[1, 0, 0, 1, 1, 0, 0, 0, 0, 0]
[0, 1, 0, 0, 1, 0, 0, 0, 0, 0]
[0, 1, 0, 0, 1, 1, 0, 0, 0, 0]
[0, 0, 1, 0, 0, 1, 0, 0, 0, 0]
[0, 0, 1, 0, 0, 1, 1, 0, 0, 0]
[0, 0, 0, 1, 0, 0, 1, 0, 0, 0]
[0, 0, 0, 1, 0, 0, 1, 1, 0, 0]
[0, 0, 0, 0, 1, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 1, 0, 0, 1, 1, 0]
[0, 0, 0, 0, 0, 1, 0, 0, 1, 0]
[0, 0, 0, 0, 0, 1, 0, 0, 1, 0]
[0, 0, 0, 0, 0, 0, 1, 0, 1, 0]
[0, 0, 0, 0, 0, 0, 0, 1, 1, 0]

Для моего моделирования важно, чтобы путешественник занимал только один ресурс, так как атрибуты узлов часто изменяются на основе этого.

Есть ли способ предотвратить такое поведение, когда путешественник никогда не занимает более одного ресурса? т.е. ресурс освобождается одновременно, когда путешественнику выделяется новый ресурс

1 ответ

Решение

На самом деле ваша модель работает правильно.

Попробуй добавить в функцию node_ocupacyтекущее время выполнения и некоторые маркеры для идентификации текущего этапа моделирования:

def node_occupancy(nodes, node, case):
    print(env.now, node, case, [node.resource.count for node in nodes])  

Кроме того, я внес некоторые изменения, чтобы увидеть лучший журнал симуляции:

def travel(self, instantiation_loc, loc):
    self.requests.append(instantiation_loc.resource.request())
    yield(self.requests[-1])

    self.location = instantiation_loc
    self.destination = instantiation_loc.up_connection
    yield self.env.timeout(self.location.travel_delay)
    node_occupancy(nodes, loc, 1)

    while self.destination.up_connection != None:
        self.requests.append(self.destination.resource.request())
        node_occupancy(nodes, loc, 2)

        yield self.requests[-1]
        node_occupancy(nodes, loc, 3)
        self.location.resource.release(self.requests[0])
        node_occupancy(nodes, loc, 4)
        self.requests.pop(0)
        self.location = self.destination
        self.destination = self.location.up_connection

        yield self.env.timeout(self.location.travel_delay)
        node_occupancy(nodes, loc, 5)

Теперь запустите симуляцию с маркером для текущего узла:

env.process(Occupier(env).travel(nodes[3], 3))
env.process(Occupier(env).travel(nodes[0], 0))

Посмотрите на результаты, и вы заметите, что события (запрос / выпуск) происходят в одно и то же время, и одновременное заполнение ресурсом времени всегда = 0 (т. Е. Время между этапами "3" и "4" для одного и того же объекта будет быть всегда 0):

5 3 1 [1, 0, 0, 1, 0, 0, 0, 0, 0, 0]
5 3 2 [1, 0, 0, 1, 1, 0, 0, 0, 0, 0]
5 0 1 [1, 0, 0, 1, 1, 0, 0, 0, 0, 0]
5 0 2 [1, 1, 0, 1, 1, 0, 0, 0, 0, 0]
5 3 3 [1, 1, 0, 1, 1, 0, 0, 0, 0, 0]
5 3 4 [1, 1, 0, 0, 1, 0, 0, 0, 0, 0]
5 0 3 [1, 1, 0, 0, 1, 0, 0, 0, 0, 0]
5 0 4 [0, 1, 0, 0, 1, 0, 0, 0, 0, 0]
Другие вопросы по тегам