一、协程:html
单线程实现并发、在应用程序里控制多个任务的切换+保存状态python
优势:程序员
应用程序级别速度要远远高于操做系统的切换算法
缺点:bash
多个任务一旦有一个阻塞没有切,整个线程都阻塞在原地,该线程内的其余的任务都不能执行了markdown
一旦引入协程,就须要检测单线程下全部的IO行为, 实现遇到IO就切换,少一个都不行,觉得一旦一个任务阻塞了,整个线程就阻塞了, 其余的任务即使是能够计算,可是也没法运行了数据结构
二、协程序的目的:并发
想要在单线程下实现并发,主要用于io密集型dom
并发指的是多个任务看起来是同时运行的ide
并发=切换+保存状态
三、协程相比于线程:
最大的区别在于,协程不须要像线程那样来回的中断切换,也不须要线程的锁机制,由于线程中断或者锁机制都会对性能问题形成影响,因此协程的性能相比于线程,性能有明显的提升,尤为在线程越多的时候,优点越明显。
生成器的一个做用是相似于迭代器,每次迭代的值为yield右值(重点),还有一种是利用yield断点,而后切换到另外一个任务,下面是第一种用法的复习
def f(maxx): n, a, b = 0, 1, 1 while n < maxx: # print(b) y = yield b a, b = b, a + b n += 1 print(y) return 'error_name' # 原函数的return变成了迭代完报出错误的值(value) fi = f(6) # 将一个函数变成生成器,并赋值给fi,每次迭代的值都是yield右边的值 print(fi.__next__()) # 运行一次生成器,到yield处中断,运行下面的程序 print(fi.send('Done')) # 回到第一次运行的生成器的yield中断处,并把Done赋予yield,而后执行下面的程序,到yield再次中止 fi.send('Done') # 若是这句换成print(fi.send('Done')),则会输出3 print(fi.send('Done')) # next和send的返回值都是fi迭代器本次的值 ######################## 1 Done 2 Done Done 5
这里有一点要注意,要激活一个生成器,必定要调用next()方法或者send(None)启动生成器,而不是调用生成器的send()方法,若是直接调用send()方法会报错,同时在生成器这里,最早调用 next() 函数这一步一般称为“预激”(prime)协程(即,让协程向前执行到第一个 yield 表达式,准备好做为活跃的协程使用)。
从Python2.5 开始,咱们能够在生成器上调用两个方法,显式的把异常发给协程。 这两个方法是throw和close。
generator.throw(exc_type[, exc_value[, traceback]])
外层代码调用throw使生成器在暂停的yield表达式处抛出指定的异常。若是生成器处理了抛出的异常,代码会向前(循环)执行到下一个yield表达式,而产出的值会成为调用throw方法获得的返回值。若是没有处理,则向上冒泡(传递给外层,值传递给外一层)。
def myGenerator(): value = 1 while True: yield value value += 1 gen = myGenerator() print gen.next() print gen.next() print gen.throw(Exception, "Method throw called!")
输出结果为
1 2 Traceback (most recent call last): File "test.txt", line 11, in <module> print gen.throw(Exception, "Method throw called!") File "test.txt", line 4, in myGenerator yield value Exception: Method throw called!
外层代码的最后一句向生成器对象抛出了一个异常。可是,在生成器对象的方法时没有处理该异常的代码,所以异常会被抛出到主方法,主方法任然未处理,最终报错。
下面的示例中,添加了处理异常的代码
def myGenerator(): value = 1 while True: try: yield value value += 1 except: value = 1 gen = myGenerator() print gen.next() print gen.next() print gen.throw(Exception, "Method throw called!")
代码的输出以下
1 2 1 Exception RuntimeError: 'generator ignored GeneratorExit' in <generator object myGenerator at 0x00000000028BB900> ignored
def myGenerator(): try: yield 1 except GeneratorExit: print "myGenerator exited" gen = myGenerator() print gen.next() 输出结果为 1 myGenerator exited
上面代码的运行逻辑以下: 当调用到gen.next()方法时,会执行生成器对象方法的yield语句。此后,主程序结束,系统会自动产生一个GeneratorExit异常,被生成器对象方法的Except语句块截获。
值得一提的是,GeneratorExit异常只有在生成器对象被激活后,才有可能产生。更确切的说,须要至少调用一次生成器对象的next方法后,系统才会产生GeneratorExit异常。请看下面的代码。
def myGenerator(): try: yield 1 yield 2 except GeneratorExit: print "myGenerator exited" gen = myGenerator() del gen print "Main caller exited"
其输出结果以下:
Main caller exited
在上面的示例中,咱们都显式地捕获了GeneratorExit异常。若是该异常没有被显式捕获,生成器对象也不会把该异常向主程序抛出。由于GeneratorExit异常定义的初衷,是方便开发者在生成器对象调用结束后定义一些收尾的工做,如释放资源等。
生成器对象的close方法会在生成器对象方法的挂起处抛出一个GeneratorExit异常。GeneratorExit异常产生后,系统会继续把生成器对象方法后续的代码执行完毕。参见下面的代码。
def myGenerator(): try: yield 1 print "Statement after yield" except GeneratorExit: print "Generator error caught" print "End of myGenerator" gen = myGenerator() print gen.next() gen.close() print "End of main caller"
代码执行过程以下:
代码的输出以下:
1 Generator error caught End of myGenerator End of main caller
须要注意的是,GeneratorExit异常的产生意味着生成器对象的生命周期已经结束。所以,一旦产生了GeneratorExit异常,生成器方法后续执行的语句中,不能再有yield语句,不然会产生RuntimeError。请看下面的例子。
def myGenerator(): try: yield 1 print "Statement after yield" except GeneratorExit: print "Generator error caught" yield 3 gen = myGenerator() print gen.next() gen.close() print "End of main caller" 输出结果为 1 Generator error caught Traceback (most recent call last): File "test.txt", line 12, in <module> gen.close() RuntimeError: generator ignored GeneratorExit
注意,因为RuntimError会向主方法抛出,所以主方法最后的print语句没有执行。
有了上面的知识,咱们就能够理解为何下面的代码会抛出RuntimError错误了。
def myGenerator(): value = 1 while True: try: yield value value += 1 except: value = 1 gen = myGenerator() print gen.next() print gen.next() print gen.throw(Exception, "Method throw called!")
上面代码中,当主程序结束前,系统产生GeneratorExit异常,被生成器对象方法的except语句捕获,可是此时while语句尚未退出,所以后面还会执行“yield value”这一语句,从而发生RuntimeError。要避免这个错误很是简单,请看下面的代码。
def myGenerator(): value = 1 while True: try: yield value value += 1 except Exception: value = 1 gen = myGenerator() print gen.next() print gen.next() print gen.throw(Exception, "Method throw called!")
代码第7行的except语句声明只捕获Exception异常对象。这样,当系统产生GeneratorExit异常后,再也不被except语句捕获,继续向外抛出,从而跳出了生成器对象方法的while语句。
这里再简单说一句,GeneratorExit异常继承自BaseException类。BaseException类与Exception类不一样。通常状况下,BaseException类是全部内建异常类的基类,而Exception类是全部用户定义的异常类的基类。
生成器和协程都是经过python中的yield
的关键字实现的,不一样的是,生成器的任务指向不可控,协程能够理解为任务指向可控的生成器。
写成是:程序员可控制的并发流程,无论是进程仍是线程,其切换都是操做系统在调度,而对于协程,程序员能够控制何时切换出去,何时切换回来
生成器能够做为协程使用。协程是指一个过程,这个过程与调用方协做,产出由调用方提供的值。
尽管生成器和协程看起来很像,可是它们表明的倒是彻底不一样的设计理念。生成器是用来生成数据的,而协程从某种意义上来讲是消耗数据的。协程和迭代无关,尽管协程也会用
next
来获取数据,可是协程和迭代无关,不要尝试像使用生成器那样去迭代地使用协程。
协程(生成器)有四种状态,分别是
GEN_CREATED:等待执行
GEN_RUNNING:解释器执行
GEN_SUSPENDED:在yield表达式处暂停
GEN_CLOSED:执行结束
协程(生成器)的状态能够用inspect.getgeneratorstate()函数来肯定,这里是:inspect模块官方文档
中文方面参考以下:http://www.javashuo.com/article/p-cdwngenb-hg.html
来看下面的例子:
from inspect import getgeneratorstate from time import sleep import threading def get_state(coro): print("其余线程生成器状态:%s", getgeneratorstate(coro)) # <1> def simple_coroutine(): for i in range(3): sleep(0.5) x = yield i + 1 # <1> my_coro = simple_coroutine() print("生成器初始状态:%s" % getgeneratorstate(my_coro)) # <2> first = next(my_coro) for i in range(5): try: my_coro.send(i) print("主线程生成器初始状态:%s" % getgeneratorstate(my_coro)) # <3> t = threading.Thread(target=get_state, args=(my_coro,)) t.start() except StopIteration: print("生成器的值拉取完毕") print("生成器最后状态:%s" % getgeneratorstate(my_coro)) # <4> ################# 生成器初始状态:GEN_CREATED 主线程生成器初始状态:GEN_SUSPENDED 其余线程生成器状态:%s GEN_SUSPENDED 主线程生成器初始状态:GEN_SUSPENDED 其余线程生成器状态:%s GEN_SUSPENDED 生成器的值拉取完毕 生成器的值拉取完毕 生成器的值拉取完毕 生成器最后状态:GEN_CLOSED
装饰器加yield实现协程,生成器可实现自动循环,加上异常捕捉,生成器中的return为异常stopitertions的值
from functools import wraps def coroutine(func): @wraps(func) def primer(*args, **kwargs): gen = func(*args, **kwargs) next(gen) return gen return primer @coroutine def averager(): total = .0 count = 0 average = None while True: term = yield average total += term count += 1 average = total / count try: coro_avg = averager() # 预激装饰器 print(coro_avg.send(10)) print(coro_avg.send(20)) print(coro_avg.send(30)) coro_avg.close() print(coro_avg.send(40)) except StopIteration: print("协程已结束") except TypeError: print("传入值异常") #################### 10.0 15.0 20.0 协程已结束
以前咱们知道,生成器的返回值是异常StopIteration的值,具体获取值的方式能够如此
from collections import namedtuple Result = namedtuple('Result', 'count average') def averager(): total = 0.0 count = 0 average = None while True: term = yield # yield右边是每次迭代的值,不写则为None if term is None: break # 为了返回值,协程必须正常终止;这里是退出条件 total += term count += 1 average = total/count # 返回一个namedtuple,包含count和average两个字段。在python3.3前,若是生成器返回值,会报错 return Result(count, average) >>> coro_avg = averager() >>> next(coro_avg) >>> coro_avg.send(20) # 并无返回值 >>> coro_avg.send(30) >>> coro_avg.send(40) >>> try: ... coro_avg.send(None) ... except StopIteration as exc: ... result = exc.value ... >>> result Result(count=3, average=30)
这里获取返回值的方法很繁琐,下面引出yield from。
yield from 结果会在内部自动捕获StopIteration 异常。这种处理方式与 for 循环处理StopIteration异常的方式同样。 对于yield from 结构来讲,解释器不只会捕获StopIteration异常,还会把value属性的值变成yield from 表达式的值。
在函数外部不能使用yield from(yield也不行)。
yield from 是 Python3.3 后新加的语言结构。和其余语言的await关键字相似,它表示:*在生成器 gen 中使用 yield from subgen()时,subgen 会得到控制权,把产出的值传给gen的调用方,即调用方能够直接控制subgen。于此同时,gen会阻塞,等待subgen终止,subgen是子生成器。
yield from 可用于简化for循环中的yield表达式。
例如:
def gen(): for c in 'AB': yield c for i in range(1, 3): yield i list(gen()) ['A', 'B', '1', '2']
能够改写为:
def gen(): yield from 'AB' yield from range(1, 3) list(gen()) ['A', 'B', '1', '2']
还有一个稍微复杂点的例子
from collections import Iterable def flatten(items, ignore_types=(str, bytes)): for x in items: if isinstance(x, Iterable) and not isinstance(x, ignore_types): # yield from x(subgen)表达式对x对象作的第一件事是,调用iter(x),获取迭代器,因此要求x是可迭代对象,每次迭代的值为yield的右值。 yield from flatten(x) # 这里递归调用,若是flatten(x)中参数x是可迭代对象,继续分解 print('委派器从yield from阻塞中还原') else: yield x # 这里是每次迭代的值,直接传给调用者,跳过(不通过)委派者管道 items = [1, 2, [3, 4, [5, 6], 7], 8] # Produces 1 2 3 4 5 6 7 8 for x in flatten(items): # 第一步yield的右值 print(x) # x为子生成器中每次迭代的 print('调用者对委派器进行了一次迭代,委派器返回一个值') items = ['Dave', 'Paula', ['Thomas', 'Lewis']] for x in flatten(items): print(x)
这里的注释看不懂先往下看,理解了yield from的委派器、管道做用就好理解了。
上面的例子若是进行了断点调试的话,你看你会发如今子生成器(subgen)中,yield x直接能吧值传给最外层的for循环,这就涉及到下面要说的yield from是链接子生成器和调用者的一个通道
PEP380 的标题是 ”syntax for delegating to subgenerator“(把指责委托给子生成器的句法)。由此咱们能够知道,yield from是能够实现嵌套生成器的使用。注意,使用 yield from 句法调用协程时,会自动预激。
yield from 的主要功能是打开双向通道,把最外层的调用方与最内层的子生成器链接起来,使二者能够直接发送和产出值,还能够直接传入异常,而不用在中间的协程添加异常处理的代码。
yield from 包含几个概念:
包含yield from 表达式的生成器函数
从yield from 部分获取的生成器。
调用委派生成器的客户端(调用方)代码
这个示意图(图一)是对yield from 的调用过程
上面的图难以理解能够看下面这个简易图
这两张图说明了,委派生成器能够理解为管道,send和yield直接在调用方和子生成器(subgen)进行交互,send能够直接把值由调用方“穿过(不通过)“委派生成器直接传递给子生成器,子生成器的yield也能够把yield右边的值穿过(不通过)委派生成器直接传递给调用者。
yield from运行的全过程能够归纳为:委派生成器在 yield from 表达式处暂停时,调用方能够直接把数据发给字生成器,子生成器再把产出的值发送给调用方。子生成器返回以后,解释器会抛出StopIteration异常,并把返回值附加到异常对象上,这时委派生成器恢复。
下面看一个例子来理解yield from的运行方式,这段代码从一个字典中读取男生和女生的身高和体重。而后把数据传给以前定义的 averager 协程,最后生成一个报告。
from collections import namedtuple Result = namedtuple('Result', 'count average') # 子生成器 # 这个例子和上边示例中的 averager 协程同样,只不过这里是做为子生成器使用 def averager(): total = 0.0 count = 0 average = None while True: # main 函数发送数据到这里 term = yield # yield右边若是有数据,则这个数据会直接传递给main,main中委派生成器迭代一次,本次迭代的值是yield的右值 if term is None: # 终止条件 break total += term count += 1 average = total/count return Result(count, average) # 返回的Result 会成为grouper函数中yield from表达式的值 # 委派生成器 def grouper(results, key): # 这个循环每次都会新建一个averager 实例,每一个实例都是做为协程使用的生成器对象 while True: # grouper 发送的每一个值都会经由yield from 处理,经过管道传给averager 实例。grouper会在yield from表达式处暂停,等待averager实例处理客户端发来的值。averager实例运行完毕后,返回的值绑定到results[key] 上。while 循环会不断建立averager实例,处理更多的值。 results[key] = yield from averager() # 调用方 def main(data): results = {} for key, values in data.items(): # group 是调用grouper函数获得的生成器对象,传给grouper 函数的第一个参数是results,用于收集结果;第二个是某个键 group = grouper(results, key) next(group) for value in values: # 把各个value传给grouper 传入的值最终到达averager函数中; # grouper并不知道传入的是什么,同时grouper实例在yield from处暂停 group.send(value) # 把None传入groupper,传入的值最终到达averager函数中,致使当前实例终止。而后继续建立下一个实例。 # 若是没有group.send(None),那么averager子生成器永远不会终止,委派生成器也永远不会在此激活,也就不会为result[key]赋值 group.send(None) report(results) # 输出报告 def report(results): for key, result in sorted(results.items()): group, unit = key.split(';') print('{:2} {:5} averaging {:.2f}{}'.format(result.count, group, result.average, unit)) data = { 'girls;kg':[40, 41, 42, 43, 44, 54], 'girls;m': [1.5, 1.6, 1.8, 1.5, 1.45, 1.6], 'boys;kg':[50, 51, 62, 53, 54, 54], 'boys;m': [1.6, 1.8, 1.8, 1.7, 1.55, 1.6], } if __name__ == '__main__': main(data)
执行结果为
6 boys averaging 54.00kg 6 boys averaging 1.68m 6 girls averaging 44.00kg 6 girls averaging 1.58m
这断代码展现了yield from 结构最简单的用法。委派生成器至关于管道,因此能够把任意数量的委派生成器链接在一块儿—一个委派生成器使用yield from 调用一个子生成器,而那个子生成器自己也是委派生成器,使用yield from调用另外一个生成器。最终以一个只是用yield表达式的生成器(或者任意可迭代对象)结束。
PEP380 分6点说明了yield from 的行为。
PEP380 还有个说明:
In a generator, the statement return value is semantically equivalent to raise StopIteration(value) except that, as currently, the exception cannot be caught by except clauses within the returning generator.
这也就是为何 yield from 可使用return 来返回值而 yield 只能使用 try … except StopIteration … 来捕获异常的value 值。
会建立几辆出租车,每辆出租车会拉几个乘客,而后回家。出租车会首先驶离车库,四处徘徊,寻找乘客;拉到乘客后,行程开始;乘客下车后,继续四处徘徊。
import random
import collections
import queue
import argparse
DEFAULT_NUMBER_OF_TAXIS = 3
DEFAULT_END_TIME = 180
SEARCH_DURATION = 5
TRIP_DURATION = 20
DEPARTURE_INTERAVAL = 5
# time 是事件发生的仿真时间,proc 是出租车进程实例的编号,action是描述活动的字符串
Event = collections.namedtuple('Event', ['time', 'proc', 'action'])
# 开始 出租车进程
# 每辆出租车调用一次taxi_process 函数,建立一个生成器对象,表示各辆出租车的运营过程。
def taxi_process(ident, trips, start_time=0):
"""
每次状态变化时向建立事件,把控制权交给仿真器
:param ident: 出租车编号
:param trips: 出租车回家前的行程数量,接客数
:param start_time: 离开车库的时间
:return:
"""
time = yield Event(start_time, ident, 'leave garage') # 产出的第一个Event
for i in range(trips): # 每次行程都会执行一遍这个代码块
# 产出一个Event实例,表示拉到了乘客 协程在这里暂停 等待下一次send() 激活
time = yield Event(time, ident, 'pick up passenger')
# 产出一个Event实例,表示乘客下车 协程在这里暂停 等待下一次send() 激活
time = yield Event(time, ident, 'drop off passenger')
# 指定的行程数量完成后,for 循环结束,最后产出 'going home' 事件。协程最后一次暂停
yield Event(time, ident, 'going home')
# 协程执行到最后 抛出StopIteration 异常
def compute_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('Unkonw previous_action: %s' % previous_action)
return int(random.expovariate(1 / interval)) + 1
# 开始仿真
class Simulator:
def __init__(self, procs_map):
self.events = queue.PriorityQueue() # 带优先级的队列 会按时间正向排序
self.procs = dict(procs_map) # 从获取的procs_map 参数中建立本地副本,为了避免修改用户传入的值
def run(self, end_time):
"""
调度并显示事件,直到时间结束
:param end_time: 结束时间 只须要指定一个参数
:return:
"""
# 调度各辆出租车的第一个事件
for iden, proc in sorted(self.procs.items()):
first_event = next(proc) # 预激协程 并产出一个 Event 对象
self.events.put(first_event) # 把各个事件加到self.events 属性表示的 PriorityQueue对象中
# 这次仿真的主循环
sim_time = 0 # 把 sim_time 归0
while sim_time < end_time:
if self.events.empty(): # 事件所有完成后退出循环
print('*** end of event ***')
break
current_event = self.events.get() # 获取优先级最高(time 属性最小)的事件
sim_time, proc_id, previous_action = current_event # 更新 sim_time
print('taxi:', proc_id, proc_id * ' ', current_event)
active_proc = self.procs[proc_id] # 从self.procs 字典中获取表示当前活动的出租车协程
next_time = sim_time + compute_duration(previous_action)
try:
next_event = active_proc.send(next_time) # 把计算获得的时间发送给出租车协程。协程会产出下一个事件,或者抛出 StopIteration
except StopIteration:
del self.procs[proc_id] # 若是有异常 表示已经退出, 删除这个协程
else:
self.events.put(next_event) # 若是没有异常,把next_event 加入到队列
else: # 若是超时 则走到这里
msg = '*** end of simulation time: {} event pendding ***'
print(msg.format(self.events.qsize()))
def main(end_time=DEFAULT_END_TIME, num_taxis=DEFAULT_NUMBER_OF_TAXIS, seed=None):
"""初始化随机生成器,构建过程,运行仿真程序"""
if seed is not None:
random.seed(seed) # 获取可复现的结果
# 构建taxis 字典。值是三个参数不一样的生成器对象。
taxis = {i: taxi_process(i, (i + 1) * 2, i * DEPARTURE_INTERAVAL)
for i in range(num_taxis)}
sim = Simulator(taxis)
sim.run(end_time)
if __name__ == '__main__':
# parser = argparse.ArgumentParser(description='Taxi fleet simulator.')
# parser.add_argument('-e', '--end-time', type=int,
# default=DEFAULT_END_TIME,
# help='simulation end time; default=%s' % DEFAULT_END_TIME)
# parser.add_argument('-t', '--taxis', type=int,
# default=DEFAULT_NUMBER_OF_TAXIS,
# help='number of taxis running; default = %s' % DEFAULT_NUMBER_OF_TAXIS)
# parser.add_argument('-s', '--seed', type=int, default=None,
# help='random generator seed (for testing)')
#
# args = parser.parse_args()
main()
运行程序
# -s 3 参数设置随机生成器的种子,以便调试的时候随机数不变,输出相同的结果 python taxi_sim.py -s 3
输出结果以下图
从结果咱们能够看出,3辆出租车的行程是交叉进行的。不一样颜色的箭头表明不一样出租车从乘客上车到乘客下车的跨度。
从结果能够看出:
咱们先在控制台中调用taxi_process 函数,本身驾驶一辆出租车,示例以下:
In [1]: from taxi_sim import taxi_process # 建立一个生成器,表示一辆出租车 编号是13 从t=0 开始,有两次行程 In [2]: taxi = taxi_process(ident=13, trips=2, start_time=0) In [3]: next(taxi) # 预激协程 Out[3]: Event(time=0, proc=13, action='leave garage') # 发送当前时间 在控制台中,变量_绑定的是前一个结果 # _.time + 7 是 0 + 7 In [4]: taxi.send(_.time+7) Out[4]: Event(time=7, proc=13, action='pick up passenger') # 这个事件有for循环在第一个行程的开头产出 # 发送_.time+12 表示这个乘客用时12分钟 In [5]: taxi.send(_.time+12) Out[5]: Event(time=19, proc=13, action='drop off passenger') # 徘徊了29 分钟 In [6]: taxi.send(_.time+29) Out[6]: Event(time=48, proc=13, action='pick up passenger') # 乘坐了50分钟 In [7]: taxi.send(_.time+50) Out[7]: Event(time=98, proc=13, action='drop off passenger') # 两次行程结束 for 循环结束产出'going home' In [8]: taxi.send(_.time+5) Out[8]: Event(time=103, proc=13, action='going home') # 再发送值,会执行到末尾 协程返回后 抛出 StopIteration 异常 In [9]: taxi.send(_.time+10) --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-9-d775cc8cc079> in <module>() ----> 1 taxi.send(_.time+10) StopIteration:
在这个示例中,咱们用控制台模拟仿真主循环。从taxi协程中产出的Event实例中获取 .time 属性,随意加一个数,而后调用send()方法发送两数之和,从新激活协程。
在taxi_sim.py 代码中,出租车协程由 Simulator.run 方法中的主循环驱动。
Simulator 类的主要数据结构以下:
self.events
PriorityQueue 对象,保存Event实例。元素能够放进PriorityQueue对象中,而后按 item[0](对象的time 属性)依序取出(按从小到大)。
self.procs
一个字典,把出租车的编号映射到仿真过程的进程(表示出租车生成器的对象)。这个属性会绑定前面所示的taxis字典副本。
优先队列是离散事件仿真系统的基础构件:建立事件的顺序不定,放入这种队列后,能够按各个事件排定的顺序取出。
好比,咱们把两个事件放入队列:
Event(time=14, proc=0, action='pick up passenger') Event(time=10, proc=1, action='pick up passenger')
这个意思是 0号出租车14分拉到一个乘客,1号出租车10分拉到一个乘客。可是主循环获取的第一个事件将是
Event(time=10, proc=1, action=‘pick up passenger’)
下面咱们分析一下仿真系统的主算法–Simulator.run 方法。
咱们代码中 while 循环有一个else 语句,仿真系统到达结束时间后,代码会执行else中的语句。
这个示例主要是想说明如何在一个主循环中处理事件,以及如何经过发送数据驱动协程,同时解释了如何使用生成器代替线程和回调,实现并发。
本文参考http://blog.gusibi.com/post/python-coroutine-yield-from/