使用协程作面向事件编程时,协程会不断的把控制权让步给主循环,激活并向前运行其余协程,从而执行各个并发活动。这是一种协做多任务:协程显示的把控制权让步给中央调度程序。编程
仿真示例数组
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) #经过上面函数的属性的到输入的参数,属性能够是双横线后的字符串也能够是添加参数函数的第一个不加横线的字符串