Python SimPy 仿真系列 (2)

这次文章是关于如何用 SimPy 来解决两个仿真需求:

  • 如何随时中断恢复 Process (进程)
  • 如何动态设置 Resource (资源)的数量

相应地这两个需求满足的场景是:

  • 仿真过程中, 某一工序被中断, 中断可以依据一个预先设定的时间或者是不确定时间
  • 仿真过程中, 人力资源也是依据时间变化, 模拟现实中工人的排班安排

回顾资源和进程的概念

ResourceProcess 是 SimPy 对人力资源和进程进行抽象的构造. Resource 好比一个队列, 其长度就是提前设置好的资源数, 不同的工序就按照时间先后和赋予的优先级进入队列. Process 从构造上来说就是一个生成器, 我们可以通过 send 方法传入 ExceptionProcess 进行打断.

比如某个工序需要占用一个工人, 耗时 30 min 来完成一个进程, 当前所有可以调用的工人数是 10, 代码形式如下:

import simpy
import random


WORKERNUM = 10 # 工人数
PROCESS_TIME = 30 * 60 # 工序耗时, 使用秒作为单位
MEAN_  = 4 * 60 # 平均物件生成时间

def process(env, workers, store):
    """工序"""
    while True:
        with workers.request() as req:
            yield req
            item = yield store.get()
            print(f"{env.now} - {item} - start")
            yield env.timeout(PROCESS_TIME)
            print(f"{env.now} - {item} - done")

def put_item(env, store):
    """每隔一段时间生成一个物品"""
    for i in range(100):
        item = f"{i}_item"
        store.put(item)
        yield env.timeout(random.expovariate(1 / MEAN_))

env = simpy.Environment()
workers = simpy.Resource(env, 10)
store = simpy.Store(env)

env.process(process(env, workers, store))
env.process(put_item(env, store))
env.run()

更详细的介绍和资料可以回顾之前的文章 Python SimPy 仿真系列 (1)

Process 进程的动态调整

存在以下两种情景:

  • 进程随时中断以及恢复
  • 按照时间表对进程进行启动或者终止

要区分一件事情, 中断的时候是让当前进程完成后再中断, 还是立即中断. 具体场景可以想象为一个工人被调离当前岗位, 他应该是先完成手头上的工序, 或者他需要停下手头的工作离开工位.

如果是必须实现进程的随时中断, 只能通过 process.interrupt() 中断 process, 即第一种场景; 假若中断是按照时间表进行, 就可以通过第二种场景, 构建多个不同时间开启的进程来进行模拟.

进程中断的实现

from simpy import interrupt, Environment

env = Environment()

def interrupter(env, victim_proc):
    yield env.timeout(1)
    victim_proc.interrupt('Spam')

def victim(env):
    try:
        yield env.timeout(10)
     except Interrupt as interrupt:
         cause = interrupt.cause

多段进程模拟按时间安排的开关


import simpy

PROCESS_TIME = 3

def put_item(env, store):
    for i in range(20):
        yield env.timeout(0.5)
        store.put(f"{i}_item")

def process(i, env, store, start, end):

    yield env.timeout(start)
    while True:
        item = yield store.get()
        # 判断 item 到达时间是否超出本进程关闭时间
        if env.now > end:
            print(f"{env.now} - process {i} - end")
            store.put(item)
            env.exit()
        else:
            print(f"{env.now} - {item} - start")
            yield env.timeout(PROCESS_TIME)
            print(f"{env.now} - {item} - end")

env = simpy.Environment()
store = simpy.Store(env)

env.process(put_item(env, store))

for i, (start, end) in enumerate([(20, 30), (40, 50), (60, 90)]):
    env.process(process(i, env, store, start, end))

env.run()

Resource 资源的动态调整

  • 资源人数按指定的排版表调配

由于Resource 在实例化后, 就没办法修改了. 为了满足在仿真过程中对资源进行修改, 使用了一个反向的思路. 首先所有资源使用 PriorityResource 实例, 预先设置一个可以调节的最大资源数, 当需要调节资源数的时候, 使用一个优先级为 -1request 去占用资源, 而正常的进程默认优先级是 0.

通过这样的操作会使得, 我们调节资源的占用进程优先级更高, 正常进程可以调用的资源数会变成
:

可以调用资源 = 最大资源 - 占用资源

import simpy

PROCESS_TIME = 2

def put_item(env, store):
    for i in range(20):
        yield env.timeout(0.5)
        store.put(f"{i}_item")

def process(env, store, resource):
    while True:
        item = yield store.get()
        with resource.request() as req:
            yield req
            yield env.timeout(PROCESS_TIME)

def set_resource(env, resource, start_time, end_time):
    """占用资源,模拟资源减少的情况,
    end_time 会出现 np.inf 无穷大,
    simpy 只会用作为排序,可以放在timeout事件里。
    """
    duration = end_time - start_time
    yield env.timeout(start_time)
    with resource.request(priority=-1) as req:
        yield req
        yield env.timeout(duration)

env = simpy.Environment()
store = simpy.Store(env)
res = simpy.PriorityResource(env, 10)

res_time_table = [(10, 20, 5), (20, 30, 6)]
env.process(put_item(env, store))
env.process(process(env, store, res))

for start, end, target_num in res_time_table:
    place_holder = 10 - target_num
    for _ in range(place_holder):
        env.process(set_resource(env, res, start, end))

env.run()
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,049评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,478评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,109评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,097评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,115评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,280评论 1 279
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,748评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,398评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,553评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,440评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,487评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,176评论 3 317
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,750评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,821评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,049评论 1 257
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,559评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,150评论 2 341

推荐阅读更多精彩内容