目录
1. 协程,asyncio包的基础构件
进程和线程是由程序触发操作系统后执行的,而协程的操作则是由程序员开发者自己定义的
对于多线程应用过CPU通过切片的方式来切换线程间的执行,而协程只使用一个线程,由我们来规定代码块的执行顺序
从语法上来看,协程类似于生成器,需要包含yield关键字,与生成器不同的是yield通常出现在表达式的右边,可以生成值,也可以不生成值。
yield在协程中只是一种流程控制工具,使用它实现协作式多任务:把控制器交给中心调度程序,从而激活其他协程
1.1 协程的四种状态
GEN_CREATED----等待开始执行
GEN_RUNNING----解释器开始执行
GEN_SUSPENED----在yield表达式处暂停
GEN_CLOSED----执行结束
1.2 两个简单的协程案例
1.协程使用生成器函数,需要定义yield
2.yield在表达式中使用, 若果协程只需从客户那里接受数据, 那么产出的值为None, 这个值是隐式之低昂的,因为yield关键字邮编没有表达式
3.首先要调用next()方法(启动生成器),或称为预激协程,让协程向前执行到第一个yield表达式,准备好作为活跃的协程供我们使用
4.调用send方法,将值传递给yield的变量,然后恢复协程,直到运行到下一个表达式
def simple_coroutine():
print('-> start')
x = yield
print('-> recived', x)
sc = simple_coroutine()
next(sc)
sc.send('666')
# 使用协程计算平均移动值
import inspect
def averager():
total = 0.0
count = 0
avg = None
while True:
num = yield avg
total += num
count += 1
avg = total/count
ag = averager()
print(next(ag))
print(ag.send(10)) # 打印avg
print(ag.send(20))
2. 显式的将异常发送给协程:
2.1 generator.throw(exc_type[, exc_value[, traceback]])
使生成器在暂停的yield表达式处抛出指定的异常,若果生成器处理了抛出的异常,name代码会向前执行到下一个yield表达式
而产出的值会成为调用generator.thow方法得到的返回值,如果生成器没有处理抛出的异常,name异常会向上冒泡,传到调用方的上下文中
2.2 generator.close()
使生成器在暂停的yield表达式处抛出CeneratorExit异常, 如果生成器没有处理这个异常,或者抛出了StopIteration异常,则调用方不会报错
如果收到GeneratorExit异常,则生成器一定不能产出值,否则解释器会抛出RuntimeErrot异常。
生成器抛出的其他异常会向上冒泡
2.1 异常处理实例
import inspect
class DemoException(Exception):
"""
自定义异常
"""
def handle_exception():
print('-> start')
while True:
try:
x = yield
except DemoException:
print('run demoException')
else:
print('recived x:', x)
raise RuntimeError('this line should never run')
he = handle_exception()
next(he)
he.send(10)
he.send(20)
he.throw(DemoException)
he.send(40)
he.close()
3. 使用yield from获取协程的返回值
为了得到返回值,协程必须正常终止,然后生成器对象抛出StopIteration异常,异常对象的value属性保存着返回的值。
yield from结构会在内部自动捕获StopIteration异常。
对yield结构来说, 解释器不仅会捕获StopIteration异常,还会将value属性的值变成yield from表达式的值
yield from的主要功能时打开双向通道,是最外层的调用方与最内层的生成器能够直接发送和产出值
3.1 yield from案例
import inspect
from collections import namedtuple
ResClass = namedtuple('Res', 'count average') # 声明一个具名元组 参数:元组名, '元素名1 元素名2......'
# 子生成器
def averager():
"""
求平均值及元素个数
:return:
"""
total = 0.0
count = 0
average = None
while True:
term = yield
if term is None:
break
total += term
count += 1
average = total / count
return ResClass(count, average)
# 委派生成器
def grouper(storages, key):
while True:
# 获取averager()返回值
storages[key] = yield from averager()
# 客户端
def client():
process_data = {
'body_1': [33.3,22.2,56.2,99.5,66.2,12.3,14.5],
'body_2': [78.6,56.3,12.4,85.2,74.1,96.3,32.1]
}
storages = {}
for k, v in process_data.items():
"""
此for循环每次迭代都会新建一个grouper实例
"""
# 获得协程
coroutine = grouper(storages, k)
# 预激协程
next(coroutine) # 此时在 yield from处暂停
for dt in v:
coroutine.send(dt) # 将dt传给子生成器averager,此时含在执行子生成器,storages[key] = 还未执行
# 此循环结束后子生成器将抛出StopIteration异常并将返回的数据包含在异常对象的value中,
# yield from可以直接爬取StopItration异常,并将异常对象的value赋值给results[key]
# 终止协程
coroutine.send(None)
print(storages)
client()
4. 协程应用实例-出租车控制台
# 使用协程代替线程进行并发活动
import collections
import queue
import random
# time 事件发生的仿真时间
# proc 出租车进程的实例编号
# action 描述活动的字符串
Event = collections.namedtuple('Event', 'time proc action')
def taxi_process(proc_num, trips_num, start_time=0):
"""
每次时间改变时,交出控制权
:param proc_num:
:param trips_num:
:param start_time:
:return:
"""
time = yield Event(start_time, proc_num, 'leave garage')
for i in range(trips_num):
time = yield Event(time, proc_num, 'pick up people')
time = yield Event(time, proc_num, 'drop off people')
yield Event(time, proc_num, 'go home')
class SimulateTaxi(object):
"""
模拟出租车控制台
"""
def __init__(self, proc_map):
# 优先级队列,保存事件,为事件排序
self.events = queue.PriorityQueue()
# 构建本地副本
self.procs = dict(proc_map)
def run(self, end_time):
"""
排序并显示事件
:param end_time:
:return:
"""
for _, taxi_gen in self.procs.items():
leave_evt = next(taxi_gen)
self.events.put(leave_evt)
# 仿真系统的主循环
simulate_time = 0
while simulate_time < end_time:
if self.events.empty():
print('*** end of events ***')
break
# 第一个事件的发生
current_evt = self.events.get()
simulate_time, proc_num, action = current_evt
print('taxi:', proc_num, ', at time: ', simulate_time, ', ', action)
# 准备下个事件的发生
proc_gen = self.procs[proc_num]
next_simulate_time = simulate_time + self.compute_duration()
try:
next_evt = proc_gen.send(next_simulate_time)
except StopIteration:
del self.procs[proc_num]
else:
self.events.put(next_evt)
else:
msg = '*** end of simulation time: {} pending ***'
print(msg.format(self.events.qsize()))
@staticmethod
def compute_duration():
"""
随机产生下一个事件的发生时间
:return:
"""
duration_time = random.randint(1, 20)
return duration_time
# 生成3个出租车,现在全部都没有离开garage
taxis = {i: taxi_process(i, (i + 1) * 2, i * 5) for i in range(3)}
# 模拟运行
st = SimulateTaxi(taxis)
st.run(100)