【SimPy系列博客之官方example学习与解读】—— Example 4: Event Latency

Hello,CSDN的各位小伙伴们,又见面啦!今天我们要学习的例程是:Event Latency!我们开始吧!

文章目录

例程背景

今天这个example比较好玩,有点类似于网络中的通信。我们要实现的是一个简单的point-to-point的消息收发机制。其中,sender源源不断地通过电缆cable给另一端的receiver发送消息,然而消息在电缆中的传输是有延迟的,因此我们还需要模拟这一种延迟。而我们需要思考的则是如何把这种延迟的建模和收发双发的processes分离开

例程代码分析

我们跳过基本的头文件和参数设置:

python 复制代码
import simpy

SIM_DURATION = 100

接下来,我们对电缆进行建模,我们思考一下电缆cable类中需要有什么功能呢?第一个我们能够发送消息到电缆上;第二,能够模拟消息传输过程中的延迟;第三,接收方能够从电缆上获取数据。为了实现功能1、3,我们就需要使用simpy中的store资源了:

python 复制代码
class Cable:
    def __init__(self, env, delay):
        self.env = env
        self.delay = delay
        self.store = simpy.Store(env)

    def latency(self, value):
        yield self.env.timeout(self.delay)  # 模拟传输过程中的时延
        self.store.put(value)  # 向电缆中传输数据

    def put(self, value):
        self.env.process(self.latency(value))

    def get(self):
        return self.store.get()  # 从电缆中获取数据

基本上比较重要的点都在注释上了,但是还有一个特别值得关注的是:在latency函数中,yield self.env.timeout(self.delay)self.store.put(value)这两句的顺序是不能调换的。不然就无法体现延迟的效果。

接下来,我们来看看收发双方怎么写:

python 复制代码
def sender(env, cable):
    while True:
        yield env.timeout(5)  # 表示每隔多久发包一次
        cable.put(f'Sender sent this message at {env.now}')

收方:

python 复制代码
def receiver(env, cable):
    while True:
        msg = yield cable.get()  # 注意要有 "yield"
        print('Receive the message: { ', msg, ' } at: ', env.now)

最后启动仿真,如下:

python 复制代码
print('EXAMPLE 4: EVENT LATENCY')
env = simpy.Environment()
cable = Cable(env, 10)
env.process(sender(env, cable))
env.process(receiver(env, cable))
env.run(until=SIM_DURATION)

输出的效果为:

EXAMPLE 4: EVENT LATENCY
Receive the message: {  Sender sent this message at 5  } at:  15
Receive the message: {  Sender sent this message at 10  } at:  20
Receive the message: {  Sender sent this message at 15  } at:  25
Receive the message: {  Sender sent this message at 20  } at:  30
Receive the message: {  Sender sent this message at 25  } at:  35
Receive the message: {  Sender sent this message at 30  } at:  40
Receive the message: {  Sender sent this message at 35  } at:  45
Receive the message: {  Sender sent this message at 40  } at:  50
Receive the message: {  Sender sent this message at 45  } at:  55
Receive the message: {  Sender sent this message at 50  } at:  60
Receive the message: {  Sender sent this message at 55  } at:  65
Receive the message: {  Sender sent this message at 60  } at:  70
Receive the message: {  Sender sent this message at 65  } at:  75
Receive the message: {  Sender sent this message at 70  } at:  80
Receive the message: {  Sender sent this message at 75  } at:  85
Receive the message: {  Sender sent this message at 80  } at:  90
Receive the message: {  Sender sent this message at 85  } at:  95

其实在这个例子中,我们相当于用一个store作为中介,来完成数据的收发。

扩展

扩展1

在上面的例子中,sender仅仅是向cable发送了一个字符串消息,如果在其他场景中,sender的一次消息中需要包含很多其他信息,要怎做呢?------ 我们可以将一个tuple put到store中,我们只需要适当修改sender和receiver函数:

python 复制代码
def sender(env, cable):
    while True:
        yield env.timeout(5)  # 表示每隔多久发包一次
        cable.put(('Message!', env.now))

def receiver(env, cable):
    while True:
        msg = yield cable.get()
        print('Receive the message: { ', msg[0], ' }, sending at: ', msg[1], ' at: ', env.now)

结果如下:

EXAMPLE 4: EVENT LATENCY
Receive the message: {  Message!  }, sending at:  5  at:  15
Receive the message: {  Message!  }, sending at:  10  at:  20
Receive the message: {  Message!  }, sending at:  15  at:  25
Receive the message: {  Message!  }, sending at:  20  at:  30
Receive the message: {  Message!  }, sending at:  25  at:  35
Receive the message: {  Message!  }, sending at:  30  at:  40
Receive the message: {  Message!  }, sending at:  35  at:  45
Receive the message: {  Message!  }, sending at:  40  at:  50
Receive the message: {  Message!  }, sending at:  45  at:  55
Receive the message: {  Message!  }, sending at:  50  at:  60
Receive the message: {  Message!  }, sending at:  55  at:  65
Receive the message: {  Message!  }, sending at:  60  at:  70
Receive the message: {  Message!  }, sending at:  65  at:  75
Receive the message: {  Message!  }, sending at:  70  at:  80
Receive the message: {  Message!  }, sending at:  75  at:  85
Receive the message: {  Message!  }, sending at:  80  at:  90
Receive the message: {  Message!  }, sending at:  85  at:  95

扩展2

上面的这些例子都还是只是最简单的P2P网络,我们现在考虑一对多的通信情况:其中,有一个sender,若干个receiver,sender不停的向receiver发送消息,只不过每次消息所要发给的对象不同,有的message是发给receiver 1的,有的消息是发给receiver 2的...,message经过电缆的传输延迟后,由对应的receiver进行接收。这是一个更加复杂的情况,我们来分析一下如何写出代码。

首先,cable类我们无需改动,但是需要将sender和receiver用类的形式进行改写:

python 复制代码
class Receiver:
    def __init__(self, env, id, cable):
        self.env = env
        self.id = id  # 用来指示不同的receiver
        self.cable = cable

    def receive(self):
        msg = yield self.cable.get()
        print('Receiver: ', self.id, ' receives the message: { ', msg[0], ' }, sending at: ',
              msg[1], ' for: ', msg[2], ' at: ', env.now)

值得注意的是,目前receive函数还没有被加入到env.process()中,所以如果此时实例化该类,再启动仿真,是没有任何结果的。

接下来,我们看看sender类:

python 复制代码
class Sender:
    def __init__(self, env, id, cable):
        self.env = env
        self.id = id  # 用来指示不同的sender
        self.cable = cable
        self.process = None

    def send(self, number_of_receiver, receivers):
        while True:  # sender会一直持续发包
            yield self.env.timeout(5)  # 表示每隔多久发包一次
            
            dst_id = random.randint(0, number_of_receiver-1)  # 先随机选择一个receiver
            self.cable.put(('Message!', self.env.now, dst_id))  # 向该receiver发包
            self.process = self.env.process(receivers[dst_id].receive())

这里,我们对self.process = self.env.process(receivers[dst_id].receive())进行解释:sender在发完包之后,如果让对应的receiver能够接收到呢?我们可以直接在sender处启动对应receiver的receive()函数。receivers是一个列表,里面的元素表示每一个receiver类,dst_id表示该message是发给哪一个receiver的,因此receivers[dst_id]就表示对应的receiver类,然后将这个receive()函数加入到仿真环境中,就可以让对应的接收方从cable中取出该message。

还需要注意的是,我们虽然在send函数中,将对应receiver的接收函数加入到了env.process()中,但是send函数本身还没有加入,因此在实例化Sender后,需要把send()加到仿真环境中:

python 复制代码
print('EXAMPLE 4: EVENT LATENCY')
random.seed(2024)
env = simpy.Environment()
cable = Cable(env, 10)

# 先创建receiver
receivers = []
for i in range(NUMBER_OF_RECEIVERS):
    r = Receiver(env, i, cable)
    receivers.append(r)

# 创建一个sender
s = Sender(env, 9999, cable)
env.process(s.send(NUMBER_OF_RECEIVERS, receivers))
env.run(until=SIM_DURATION)

仿真结果如下:

EXAMPLE 4: EVENT LATENCY
Receiver:  1  receives the message: {  Message!  }, sending at:  5  for:  1  at:  15
Receiver:  0  receives the message: {  Message!  }, sending at:  10  for:  0  at:  20
Receiver:  2  receives the message: {  Message!  }, sending at:  15  for:  2  at:  25
Receiver:  2  receives the message: {  Message!  }, sending at:  20  for:  2  at:  30
Receiver:  1  receives the message: {  Message!  }, sending at:  25  for:  1  at:  35
Receiver:  0  receives the message: {  Message!  }, sending at:  30  for:  0  at:  40
Receiver:  2  receives the message: {  Message!  }, sending at:  35  for:  2  at:  45
Receiver:  1  receives the message: {  Message!  }, sending at:  40  for:  1  at:  50
Receiver:  2  receives the message: {  Message!  }, sending at:  45  for:  2  at:  55
Receiver:  1  receives the message: {  Message!  }, sending at:  50  for:  1  at:  60
Receiver:  2  receives the message: {  Message!  }, sending at:  55  for:  2  at:  65
Receiver:  0  receives the message: {  Message!  }, sending at:  60  for:  0  at:  70
Receiver:  2  receives the message: {  Message!  }, sending at:  65  for:  2  at:  75
Receiver:  2  receives the message: {  Message!  }, sending at:  70  for:  2  at:  80
Receiver:  1  receives the message: {  Message!  }, sending at:  75  for:  1  at:  85
Receiver:  1  receives the message: {  Message!  }, sending at:  80  for:  1  at:  90
Receiver:  1  receives the message: {  Message!  }, sending at:  85  for:  1  at:  95

我们可以看到,结果符合我们的要求。

扩展3

有了前面的一系列铺垫,我们很容易能够将我们的整个通信系统扩展到多对多:即多个sender给多个receiver分别发消息,下面直接贴上完整代码,应该是没有太多困难的地方了:

python 复制代码
import simpy
import random

RANDOM_SEED = 2024
NUMBER_OF_RECEIVERS = 3
SIM_DURATION = 100

class Cable:
    def __init__(self, env, delay):
        self.env = env
        self.delay = delay
        self.store = simpy.Store(env)

    def latency(self, value):
        yield self.env.timeout(self.delay)
        self.store.put(value)

    def put(self, value):
        self.env.process(self.latency(value))

    def get(self):
        return self.store.get()

class Receiver:
    def __init__(self, env, id, cable):
        self.env = env
        self.id = id
        self.cable = cable

    def receive(self):
        msg = yield self.cable.get()
        print('Receiver: ', self.id, ' receives the message: { ', msg[0], ' }, sending at: ',
              msg[1], 'from: ', msg[2], ' for: ', msg[3], ' at: ', env.now)

class Sender:
    def __init__(self, env, id, cable):
        self.env = env
        self.id = id
        self.cable = cable
        self.process = None

    def send(self, number_of_receiver, receivers):
        while True:  # sender持续发包
            yield self.env.timeout(5)  # 表示每隔多久发包一次
            # 先随机选择一个receiver
            dst_id = random.randint(0, number_of_receiver-1)
            self.cable.put(('Message!', self.env.now, self.id, dst_id))
            self.process = self.env.process(receivers[dst_id].receive())

print('EXAMPLE 4: EVENT LATENCY')
random.seed(RANDOM_SEED)
env = simpy.Environment()
cable = Cable(env, 10)

# 先创建receiver
receivers = []
for i in range(NUMBER_OF_RECEIVERS):
    r = Receiver(env, i, cable)
    receivers.append(r)

# 创建一个sender
for j in range(3):
    x = Sender(env, j+1000, cable)
    env.process(x.send(NUMBER_OF_RECEIVERS, receivers))
    
env.run(until=SIM_DURATION)

仿真结果如下:

EXAMPLE 4: EVENT LATENCY
Receiver:  1  receives the message: {  Message!  }, sending at:  5 from:  1000  for:  1  at:  15
Receiver:  0  receives the message: {  Message!  }, sending at:  5 from:  1001  for:  0  at:  15
Receiver:  2  receives the message: {  Message!  }, sending at:  5 from:  1002  for:  2  at:  15
Receiver:  2  receives the message: {  Message!  }, sending at:  10 from:  1000  for:  2  at:  20
Receiver:  1  receives the message: {  Message!  }, sending at:  10 from:  1001  for:  1  at:  20
Receiver:  0  receives the message: {  Message!  }, sending at:  10 from:  1002  for:  0  at:  20
Receiver:  2  receives the message: {  Message!  }, sending at:  15 from:  1000  for:  2  at:  25
Receiver:  1  receives the message: {  Message!  }, sending at:  15 from:  1001  for:  1  at:  25
Receiver:  2  receives the message: {  Message!  }, sending at:  15 from:  1002  for:  2  at:  25
Receiver:  1  receives the message: {  Message!  }, sending at:  20 from:  1000  for:  1  at:  30
Receiver:  2  receives the message: {  Message!  }, sending at:  20 from:  1001  for:  2  at:  30
Receiver:  0  receives the message: {  Message!  }, sending at:  20 from:  1002  for:  0  at:  30
Receiver:  2  receives the message: {  Message!  }, sending at:  25 from:  1000  for:  2  at:  35
Receiver:  2  receives the message: {  Message!  }, sending at:  25 from:  1001  for:  2  at:  35
Receiver:  1  receives the message: {  Message!  }, sending at:  25 from:  1002  for:  1  at:  35
Receiver:  1  receives the message: {  Message!  }, sending at:  30 from:  1000  for:  1  at:  40
Receiver:  1  receives the message: {  Message!  }, sending at:  30 from:  1001  for:  1  at:  40
Receiver:  2  receives the message: {  Message!  }, sending at:  30 from:  1002  for:  2  at:  40
Receiver:  2  receives the message: {  Message!  }, sending at:  35 from:  1000  for:  2  at:  45
Receiver:  2  receives the message: {  Message!  }, sending at:  35 from:  1001  for:  2  at:  45
Receiver:  0  receives the message: {  Message!  }, sending at:  35 from:  1002  for:  0  at:  45
Receiver:  1  receives the message: {  Message!  }, sending at:  40 from:  1000  for:  1  at:  50
Receiver:  2  receives the message: {  Message!  }, sending at:  40 from:  1001  for:  2  at:  50
Receiver:  2  receives the message: {  Message!  }, sending at:  40 from:  1002  for:  2  at:  50
Receiver:  1  receives the message: {  Message!  }, sending at:  45 from:  1000  for:  1  at:  55
Receiver:  2  receives the message: {  Message!  }, sending at:  45 from:  1001  for:  2  at:  55
Receiver:  0  receives the message: {  Message!  }, sending at:  45 from:  1002  for:  0  at:  55
Receiver:  2  receives the message: {  Message!  }, sending at:  50 from:  1000  for:  2  at:  60
Receiver:  0  receives the message: {  Message!  }, sending at:  50 from:  1001  for:  0  at:  60
Receiver:  2  receives the message: {  Message!  }, sending at:  50 from:  1002  for:  2  at:  60
Receiver:  2  receives the message: {  Message!  }, sending at:  55 from:  1000  for:  2  at:  65
Receiver:  1  receives the message: {  Message!  }, sending at:  55 from:  1001  for:  1  at:  65
Receiver:  2  receives the message: {  Message!  }, sending at:  55 from:  1002  for:  2  at:  65
Receiver:  2  receives the message: {  Message!  }, sending at:  60 from:  1000  for:  2  at:  70
Receiver:  0  receives the message: {  Message!  }, sending at:  60 from:  1001  for:  0  at:  70
Receiver:  2  receives the message: {  Message!  }, sending at:  60 from:  1002  for:  2  at:  70
Receiver:  0  receives the message: {  Message!  }, sending at:  65 from:  1000  for:  0  at:  75
Receiver:  1  receives the message: {  Message!  }, sending at:  65 from:  1001  for:  1  at:  75
Receiver:  0  receives the message: {  Message!  }, sending at:  65 from:  1002  for:  0  at:  75
Receiver:  1  receives the message: {  Message!  }, sending at:  70 from:  1000  for:  1  at:  80
Receiver:  2  receives the message: {  Message!  }, sending at:  70 from:  1001  for:  2  at:  80
Receiver:  1  receives the message: {  Message!  }, sending at:  70 from:  1002  for:  1  at:  80
Receiver:  1  receives the message: {  Message!  }, sending at:  75 from:  1000  for:  1  at:  85
Receiver:  0  receives the message: {  Message!  }, sending at:  75 from:  1001  for:  0  at:  85
Receiver:  2  receives the message: {  Message!  }, sending at:  75 from:  1002  for:  2  at:  85
Receiver:  2  receives the message: {  Message!  }, sending at:  80 from:  1000  for:  2  at:  90
Receiver:  0  receives the message: {  Message!  }, sending at:  80 from:  1001  for:  0  at:  90
Receiver:  1  receives the message: {  Message!  }, sending at:  80 from:  1002  for:  1  at:  90
Receiver:  1  receives the message: {  Message!  }, sending at:  85 from:  1000  for:  1  at:  95
Receiver:  1  receives the message: {  Message!  }, sending at:  85 from:  1001  for:  1  at:  95
Receiver:  1  receives the message: {  Message!  }, sending at:  85 from:  1002  for:  1  at:  95

最后,特别需要注意的是:要想实现多对多,或者一对多通信,那么这个cable一定要是在外部设的,而不能是sender类或者是receiver类里面自己重新初始化的成员!

相关推荐
hengzhepa14 分钟前
ElasticSearch备考 -- Async search
大数据·学习·elasticsearch·搜索引擎·es
_.Switch1 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一个闪现必杀技1 小时前
Python入门--函数
开发语言·python·青少年编程·pycharm
小鹿( ﹡ˆoˆ﹡ )1 小时前
探索IP协议的神秘面纱:Python中的网络通信
python·tcp/ip·php
卷心菜小温2 小时前
【BUG】P-tuningv2微调ChatGLM2-6B时所踩的坑
python·深度学习·语言模型·nlp·bug
小小洋洋2 小时前
BLE MESH学习1-基于沁恒CH582学习
学习
陈苏同学2 小时前
4. 将pycharm本地项目同步到(Linux)服务器上——深度学习·科研实践·从0到1
linux·服务器·ide·人工智能·python·深度学习·pycharm
唐家小妹2 小时前
介绍一款开源的 Modern GUI PySide6 / PyQt6的使用
python·pyqt
羊小猪~~3 小时前
深度学习项目----用LSTM模型预测股价(包含LSTM网络简介,代码数据均可下载)
pytorch·python·rnn·深度学习·机器学习·数据分析·lstm
Ace'3 小时前
每日一题&&学习笔记
笔记·学习