Simpy离散事件仿真(9)——专题指南(5)——共享资源

共享资源是进程交互的另一种方式。它们形成了一个拥塞点,进程排队使用它们。

SimPy定义了三类资源:

(1)Resources——一次可由有限数量的过程使用的资源(例如,具有有限数量燃油泵的加油站)。

(2)Containers——模拟同质、未分散的生产、消费资源。它可以是连续的(如水)或离散的(如苹果)。

(3)Stores——允许生产和使用Python对象的资源。

 

1 资源的基本概念

所有资源都有相同的基本概念:资源本身是某种容量的容器,通常是有限容量。进程可以尝试将某些内容放入资源中,也可以尝试将某些内容取出。如果资源已满或为空,则必须排队等待。

以下大致就是资源的构成:

BaseResource(capacity):
   put_queue
   get_queue

   put(): event
   get(): event

每个资源都有一个最大容量和两个队列:一个用于希望将某些内容放入其中的进程,另一个用于希望将某些内容取出的进程。put()和get()方法都返回一个事件,该事件在相应操作成功时触发。

当进程等待put或get事件成功时,它可能会被另一个进程中断。在捕捉到中断后,该进程有两种可能:

(1)它可以继续等待请求(再次生成事件)。

(2)它可能会停止等待请求。在这种情况下,必须调用事件的cancel()方法。

因为您很容易忘记这一点,所以所有资源事件都是上下文管理器(有关详细信息,请参阅Python文档)。

资源系统具有模块化和可扩展性。例如,资源可以使用专用队列和事件类型,允许使用排序队列,向事件添加优先级,或提供抢占。

 

2 Resources

资源可以同时由有限数量的进程使用(例如,具有有限数量燃油泵的加油站)。进程要求使用这些资源(或“拥有”这些资源),并且必须在完成后释放资源(例如,车辆到达加油站,使用燃油泵(如果可用),完成后离开)。

请求资源的建模行为是“将进程的令牌放入资源”,相应的,释放资源是“从资源中取出进程的令牌”。因此,调用request()/release()等同于调用put()/get()。释放资源总是会立即成功。

SimPy实现了三种Rescource类型:

(1)Resource,资源

(2)PriorityResource,有限资源,其中排队进程按优先级排序

(3)PreemptiveResource,抢占资源其中进程可以另外抢占优先级较低的其他进程

 

2.1 资源Resource

Resource在概念上是一个信号量。它唯一的参数——除了必须提到的环境之外——是它的容量。它必须是正数,默认为1:Resource(env,capacity=1)。

它不只是计算当前用户,而是将请求事件存储为每个用户的“访问令牌”。这对于添加抢占非常有用(见下文)。

下面是使用Resource的基本示例:

import simpy

def resource_user(env, resource):
    request = resource.request()  # Generate a request event
    yield request                 # Wait for access
    yield env.timeout(1)          # Do something
    resource.release(request)     # Release the resource

env = simpy.Environment()
res = simpy.Resource(env, capacity=1)
user = env.process(resource_user(env, res))
env.run()

注意,您必须在所有条件下释放资源,无论您是在等待或使用资源时被中断。为了帮助你和避免使用太多的try:...finally:...构造、请求事件可以使用上下文管理器:

def resource_user(env, resource):
    with resource.request() as req:  # Generate a request event
        yield req                    # Wait for access
        yield env.timeout(1)         # Do something
        # Resource released automatically
user = env.process(resource_user(env, res))
env.run()

资源允许您检索当前用户或排队用户的列表、当前用户的数量和资源的容量:

res = simpy.Resource(env, capacity=1)

def print_stats(res):
    print(f'{res.count} of {res.capacity} slots are allocated.')
    print(f'  Users: {res.users}')
    print(f'  Queued events: {res.queue}')


def user(res):
    print_stats(res)
    with res.request() as req:
        yield req
        print_stats(res)
    print_stats(res)

procs = [env.process(user(res)), env.process(user(res))]
env.run()
0 of 1 slots are allocated.
  Users: []
  Queued events: []
1 of 1 slots are allocated.
  Users: [<Request() object at 0x...>]
  Queued events: []
1 of 1 slots are allocated.
  Users: [<Request() object at 0x...>]
  Queued events: [<Request() object at 0x...>]
0 of 1 slots are allocated.
  Users: []
  Queued events: [<Request() object at 0x...>]
1 of 1 slots are allocated.
  Users: [<Request() object at 0x...>]
  Queued events: []
0 of 1 slots are allocated.
  Users: []
  Queued events: []

2.2 PriorityResource优先资源

从现实世界中你可能知道,并不是每个人都同等重要。把它映射到SimPy,这是PriorityResource。此资源子类允许请求进程为每个请求提供优先级。更重要的请求将比不重要的请求更早地访问资源。优先级用整数表示;较小的数字意味着较高的优先级。

除此之外,它的工作方式与普通资源类似:

def resource_user(name, env, resource, wait, prio):
    yield env.timeout(wait)
    with resource.request(priority=prio) as req:
        print(f'{name} requesting at {env.now} with priority={prio}')
        yield req
        print(f'{name} got resource at {env.now}')
        yield env.timeout(3)

env = simpy.Environment()
res = simpy.PriorityResource(env, capacity=1)
p1 = env.process(resource_user(1, env, res, wait=0, prio=0))
p2 = env.process(resource_user(2, env, res, wait=1, prio=0))
p3 = env.process(resource_user(3, env, res, wait=2, prio=-1))
env.run()
1 requesting at 0 with priority=0
1 got resource at 0
2 requesting at 1 with priority=0
3 requesting at 2 with priority=-1
3 got resource at 3
2 got resource at 6

尽管p3请求的资源比p2晚,但它可以更早地使用它,因为它的优先级更高。

 

2.3 PreemptiveResource抢占资源

有时,新的请求非常重要,以至于队列跳转还不够,它们需要将现有用户踢出资源(这称为抢占)。PreemptiveResource允许您执行以下操作:

def resource_user(name, env, resource, wait, prio):
    yield env.timeout(wait)
    with resource.request(priority=prio) as req:
        print(f'{name} requesting at {env.now} with priority={prio}')
        yield req
        print(f'{name} got resource at {env.now}')
        try:
            yield env.timeout(3)
        except simpy.Interrupt as interrupt:
            by = interrupt.cause.by
            usage = env.now - interrupt.cause.usage_since
            print(f'{name} got preempted by {by} at {env.now}'
                f' after {usage}')

env = simpy.Environment()
res = simpy.PreemptiveResource(env, capacity=1)
p1 = env.process(resource_user(1, env, res, wait=0, prio=0))
p2 = env.process(resource_user(2, env, res, wait=1, prio=0))
p3 = env.process(resource_user(3, env, res, wait=2, prio=-1))
env.run()
1 requesting at 0 with priority=0
1 got resource at 0
2 requesting at 1 with priority=0
3 requesting at 2 with priority=-1
1 got preempted by <Process(resource_user) object at 0x...> at 2 after 2
3 got resource at 2
2 got resource at 5

PreemptiveResource从PriorityResource继承,并向request()添加一个preempt标志(默认为True)。通过将其设置为False(resource.request(priority=x,preempt=False)),进程可以决定不抢占另一个资源用户。不过,它仍将根据优先级放入队列中。

抢占资源的实现优先权高于抢占。这意味着抢占请求不允许欺骗和跳过优先级更高的请求。以下示例显示,抢占式低优先级请求无法在高优先级请求上排队跳转:

def user(name, env, res, prio, preempt):
    with res.request(priority=prio, preempt=preempt) as req:
        try:
            print(f'{name} requesting at {env.now}')
            assert isinstance(env.now, int), type(env.now)
            yield req
            assert isinstance(env.now, int), type(env.now)
            print(f'{name} got resource at {env.now}')
            yield env.timeout(3)
        except simpy.Interrupt:
            print(f'{name} got preempted at {env.now}')

env = simpy.Environment()
res = simpy.PreemptiveResource(env, capacity=1)
A = env.process(user('A', env, res, prio=0, preempt=True))
env.run(until=1)  # Give A a head start
A requesting at 0
A got resource at 0
B = env.process(user('B', env, res, prio=-2, preempt=False))
C = env.process(user('C', env, res, prio=-1, preempt=True))
env.run()
B requesting at 1
C requesting at 1
B got resource at 3
C got resource at 6

(1)处理A请求优先级为0的资源。它立即成为用户。

(2)进程B请求优先级为-2的资源,但将preempt设置为False。它会排队等候。

(3)进程C请求优先级为-1的资源,但保留preempt为True。通常情况下,它会抢占A,但在这种情况下,B在C之前排队,并阻止C抢占A。由于B的优先级不够高,C也不能抢占B。

因此,示例中的行为与完全不使用抢占一样。使用混合抢占时要小心!

由于进程B的优先级较高,在本例中不发生抢占。注意,优先级为-3的附加请求可以抢占A。

如果您的用例需要不同的行为,例如队列跳转或将抢占值置于优先级之上,则可以将抢占资源子类化并重写默认行为。

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值