流暢的python:出租車仿真示例

這個簡單的例子讓咱們比較淺顯易懂的看到了事件驅動型框架的運做方式,即在單個線程中使用一個主循環驅動協程執行併發活動。

使用協程作面向事件編程時,協程會不斷的把控制權讓步給主循環,激活並向前運行其餘協程,從而執行各個併發活動。這是一種協做多任務:協程顯示的把控制權讓步給中央調度程序。編程

仿真示例數組

import random
import collections
import queue
import argparse
import time

DEFAULT_NUMBER_OF_TAXIS = 3
DEFAULT_END_TIME = 180
SEARCH_DURATION = 5
TRIP_DURATION = 20
DEPARTURE_INTERVAL = 5

Event = collections.namedtuple('Event', 'time proc action')

#出租車進程。
def taxi_process(ident, trips, start_time=0):
    """每次狀態變化時向仿真程序產出一個事件"""
    time = yield Event(start_time, ident, 'leave garage')
    for i in range(trips):
        time = yield Event(time, ident, 'pick up passenger')
        time = yield Event(time, ident, 'drop off passenger')
    yield Event(time, ident, 'going home')
    #結束出租車進程

#出租車仿真程序主程序。
class Simulator:

    def __init__(self, procs_map):
        self.events = queue.PriorityQueue()  #優先級隊列,put方法放入數據,通常是一個數組(3, someting),get()方法數值小的優先出隊
        self.procs = dict(procs_map)  #建立字典的副本

    def run(self, end_time):
        """調度並顯示事件,直到事件結束"""
        #調度各輛出租車的第一個事件
        for _, proc in sorted(self.procs.items()):
            first_event = next(proc)  #第一個事件是全部車離開車庫,也是爲了激活子生成器
            self.events.put(first_event)  #全部車的第一個事件放到優先隊列中,time小的優先出來

        #這次仿真的主循環
        sim_time = 0
        while sim_time < end_time:
            if self.events.empty():
                print('***事件結束***')
                break
            current_event = self.events.get() #取出time最小的事件
            sim_time, proc_id, previous_action = current_event  #元組解包
            print('taxi:', proc_id, proc_id * '  ', current_event)
            active_proc = self.procs[proc_id]  #取出當前事件對象。是一個子生成器對象,下面要對這個對象send(time)來得到下一個yield的返回值
            next_time = sim_time + comput_duration(previous_action)  #隨機計算下一個時間
            try:
                next_event = active_proc.send(next_time)  #下一個事件是子生成器執行到下一個yield的返回值
            except StopIteration:  #StopIteration異常說明當前子生成器執行完畢,從字典中刪除它
                del self.procs[proc_id]
            else:
                self.events.put(next_event) #不然就把下一個事件放入優先隊列中
        else: #若是while循環沒有以break結束,那麼輸出結束信息
            msg = '*** 仿真結束。{}個車沒有回家 ***'
            print(msg.format(self.events.qsize()))
        #仿真結束

def comput_duration(previous_action):
    """使用指數分佈計算操做的耗時"""
    if previous_action in ['leave garage', 'drop off passenger']:
        interval = SEARCH_DURATION
    elif previous_action == 'pick up passenger':
        #新狀態是行程開始
        interval = TRIP_DURATION
    elif previous_action == 'going home':
        interval = 1
    else:
        raise ValueError('未知的活動:{}'.format(previous_action))
    return int(random.expovariate(1/interval) + 1)

def main(end_time=DEFAULT_END_TIME, num_taxis=DEFAULT_NUMBER_OF_TAXIS, seed=None):
    #構建隨機生成器,構建過程,運行仿真程序
    if seed is not None:
        random.seed(seed)  #指定seed的值時,用這個seed能夠使隨機數隨機出來的相等
    taxis = {i: taxi_process(i, (i+1*2), i*DEPARTURE_INTERVAL) for i in range(num_taxis)} #字典生成式,生成指定數量的子生成器對象
    sim = Simulator(taxis) #實例化仿真主循環
    sim.run(end_time)  #run it!

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='出租車運行仿真')  #建立參數解析對象,添加描述
    parser.add_argument('-e', '--end_time', type=int, default=DEFAULT_END_TIME) #添加-e參數,默認值爲180
    parser.add_argument('-t', '--taxis', type=int, default=DEFAULT_NUMBER_OF_TAXIS, help='出租車出行數量, default=%s' %DEFAULT_NUMBE
R_OF_TAXIS)  #添加-t參數,用來指定出租車數量,默認值爲3
    parser.add_argument('-s', '--seed', type=int, default=None, help='隨機生成seed')  #添加-s參數,用來設置seed值,若是seed值同樣那
麼隨機出來的結果也會同樣,默認值爲None
    args = parser.parse_args()  #這個函數用來獲取參數
    main(args.end_time, args.taxis, args.seed) #經過上面函數的屬性的到輸入的參數,屬性能夠是雙橫線後的字符串也能夠是添加參數函數的第一個不加橫線的字符串
相關文章
相關標籤/搜索