Hello,CSDN的各位小伙伴们,又见面啦!今天我们要学习的例程是:Event Latency!我们开始吧!
例程背景
今天这个example比较好玩,有点类似于网络中的通信。我们要实现的是一个简单的point-to-point的消息收发机制。其中,sender源源不断地通过电缆cable给另一端的receiver发送消息,然而消息在电缆中的传输是有延迟的,因此我们还需要模拟这一种延迟。而我们需要思考的则是如何把这种延迟的建模和收发双发的processes分离开。
例程代码分析
我们跳过基本的头文件和参数设置:
import simpy
SIM_DURATION = 100
接下来,我们对电缆进行建模,我们思考一下电缆cable类中需要有什么功能呢?第一个我们能够发送消息到电缆上;第二,能够模拟消息传输过程中的延迟;第三,接收方能够从电缆上获取数据。为了实现功能1、3,我们就需要使用simpy中的store资源了:
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)
这两句的顺序是不能调换的。不然就无法体现延迟的效果。
接下来,我们来看看收发双方怎么写:
def sender(env, cable):
while True:
yield env.timeout(5) # 表示每隔多久发包一次
cable.put(f'Sender sent this message at {env.now}')
收方:
def receiver(env, cable):
while True:
msg = yield cable.get() # 注意要有 "yield"
print('Receive the message: { ', msg, ' } at: ', env.now)
最后启动仿真,如下:
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函数:
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用类的形式进行改写:
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类:
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()加到仿真环境中:
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分别发消息,下面直接贴上完整代码,应该是没有太多困难的地方了:
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)
仿真结果如下:文章来源:https://www.toymoban.com/news/detail-791547.html
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类里面自己重新初始化的成员!文章来源地址https://www.toymoban.com/news/detail-791547.html
到了这里,关于【SimPy系列博客之官方example学习与解读】—— Example 4: Event Latency的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!