Hello,CSDN的各位小伙伴們,又見面啦!今天我們要學(xué)習(xí)的例程是:Event Latency!我們開始吧!
例程背景
今天這個example比較好玩,有點類似于網(wǎng)絡(luò)中的通信。我們要實現(xiàn)的是一個簡單的point-to-point的消息收發(fā)機制。其中,sender源源不斷地通過電纜cable給另一端的receiver發(fā)送消息,然而消息在電纜中的傳輸是有延遲的,因此我們還需要模擬這一種延遲。而我們需要思考的則是如何把這種延遲的建模和收發(fā)雙發(fā)的processes分離開。
例程代碼分析
我們跳過基本的頭文件和參數(shù)設(shè)置:
import simpy
SIM_DURATION = 100
接下來,我們對電纜進行建模,我們思考一下電纜cable類中需要有什么功能呢?第一個我們能夠發(fā)送消息到電纜上;第二,能夠模擬消息傳輸過程中的延遲;第三,接收方能夠從電纜上獲取數(shù)據(jù)。為了實現(xiàn)功能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) # 向電纜中傳輸數(shù)據(jù)
def put(self, value):
self.env.process(self.latency(value))
def get(self):
return self.store.get() # 從電纜中獲取數(shù)據(jù)
基本上比較重要的點都在注釋上了,但是還有一個特別值得關(guān)注的是:在latency函數(shù)中,yield self.env.timeout(self.delay)
和self.store.put(value)
這兩句的順序是不能調(diào)換的。不然就無法體現(xiàn)延遲的效果。
接下來,我們來看看收發(fā)雙方怎么寫:
def sender(env, cable):
while True:
yield env.timeout(5) # 表示每隔多久發(fā)包一次
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作為中介,來完成數(shù)據(jù)的收發(fā)。
擴展
擴展1
在上面的例子中,sender僅僅是向cable發(fā)送了一個字符串消息,如果在其他場景中,sender的一次消息中需要包含很多其他信息,要怎做呢?—— 我們可以將一個tuple put到store中,我們只需要適當修改sender和receiver函數(shù):
def sender(env, cable):
while True:
yield env.timeout(5) # 表示每隔多久發(fā)包一次
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)
結(jié)果如下:
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網(wǎng)絡(luò),我們現(xiàn)在考慮一對多的通信情況:其中,有一個sender,若干個receiver,sender不停的向receiver發(fā)送消息,只不過每次消息所要發(fā)給的對象不同,有的message是發(fā)給receiver 1的,有的消息是發(fā)給receiver 2的…,message經(jīng)過電纜的傳輸延遲后,由對應(yīng)的receiver進行接收。這是一個更加復(fù)雜的情況,我們來分析一下如何寫出代碼。
首先,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函數(shù)還沒有被加入到env.process()中,所以如果此時實例化該類,再啟動仿真,是沒有任何結(jié)果的。
接下來,我們看看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會一直持續(xù)發(fā)包
yield self.env.timeout(5) # 表示每隔多久發(fā)包一次
dst_id = random.randint(0, number_of_receiver-1) # 先隨機選擇一個receiver
self.cable.put(('Message!', self.env.now, dst_id)) # 向該receiver發(fā)包
self.process = self.env.process(receivers[dst_id].receive())
這里,我們對self.process = self.env.process(receivers[dst_id].receive())
進行解釋:sender在發(fā)完包之后,如果讓對應(yīng)的receiver能夠接收到呢?我們可以直接在sender處啟動對應(yīng)receiver的receive()函數(shù)。receivers是一個列表,里面的元素表示每一個receiver類,dst_id表示該message是發(fā)給哪一個receiver的,因此receivers[dst_id]
就表示對應(yīng)的receiver類,然后將這個receive()函數(shù)加入到仿真環(huán)境中,就可以讓對應(yīng)的接收方從cable中取出該message。
還需要注意的是,我們雖然在send函數(shù)中,將對應(yīng)receiver的接收函數(shù)加入到了env.process()中,但是send函數(shù)本身還沒有加入,因此在實例化Sender后,需要把send()加到仿真環(huán)境中:
print('EXAMPLE 4: EVENT LATENCY')
random.seed(2024)
env = simpy.Environment()
cable = Cable(env, 10)
# 先創(chuàng)建receiver
receivers = []
for i in range(NUMBER_OF_RECEIVERS):
r = Receiver(env, i, cable)
receivers.append(r)
# 創(chuàng)建一個sender
s = Sender(env, 9999, cable)
env.process(s.send(NUMBER_OF_RECEIVERS, receivers))
env.run(until=SIM_DURATION)
仿真結(jié)果如下:
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
我們可以看到,結(jié)果符合我們的要求。
擴展3
有了前面的一系列鋪墊,我們很容易能夠?qū)⑽覀兊恼麄€通信系統(tǒng)擴展到多對多:即多個sender給多個receiver分別發(fā)消息,下面直接貼上完整代碼,應(yīng)該是沒有太多困難的地方了:
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持續(xù)發(fā)包
yield self.env.timeout(5) # 表示每隔多久發(fā)包一次
# 先隨機選擇一個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)
# 先創(chuàng)建receiver
receivers = []
for i in range(NUMBER_OF_RECEIVERS):
r = Receiver(env, i, cable)
receivers.append(r)
# 創(chuàng)建一個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)
仿真結(jié)果如下:文章來源:http://www.zghlxwxcb.cn/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
最后,特別需要注意的是:要想實現(xiàn)多對多,或者一對多通信,那么這個cable一定要是在外部設(shè)的,而不能是sender類或者是receiver類里面自己重新初始化的成員!文章來源地址http://www.zghlxwxcb.cn/news/detail-791547.html
到了這里,關(guān)于【SimPy系列博客之官方example學(xué)習(xí)與解讀】—— Example 4: Event Latency的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!