协程
一个协程可以:
@asyncio.coroutine——标记基于生成器协程的装饰器,它允许生成器使用yield from来调用async def协程;也允许生成器被async def协程调用,比如使用await表达式。
调用协程后并不会马上执行代码,需要你调度执行。有两种方法可以让其开始运行:从其他协程中调用yield from coroutine 或 await coroutine;使用ensure_future()函数或者AbstractEventLoop.create_task()方法执行。
asyncio一些关键字的说明:
使用asyncio.sleep(1)和time.sleep(1)的区别是前者立即返回一个Future对象,下次循环时判断是否过2s,而不是阻塞等待。使用time.sleep()会导致并发效果作废。
@asyncio.coroutine
def hello():
print("Hello world!")
r=yield from asyncio.sleep(1)
print("Hello again!")
#这是一个coroutine对象,此时该函数并没有执行
coroutine=hello()
#获取eventloop
loop=asyncio.get_event_loop()
#将协程加入到事件循环loop中,执行coroutine
loop.run_until_complete(coroutine)
loop.close()
运行结果为:
整个流程就是:我们首先定义一个协程,然后创建一个事件循环,将协程注册到事件循环loop中,并且启动事件循环。
await关键字
await关键字用来对一些耗时的操作进行挂起,比如IO操作、网络请求等。协程遇到await后,事件循环会挂起该协程,然后执行下一个协程,直到其他的协程也执行或者挂起。
import asyncio
async def wget(host):
print('wget %s...' % host)
connect=asyncio.open_connection(host,80) #返回返回一个StreamReader和一个StramWriter的实例
#StreamReader是提供API向IO流写入数据的写入器对象
reader,writer=await connect
header='GET / HTTP/1.0\r\nHost:%s\r\n\r\n' %host #HTTP请求报文
writer.write(header.encode('utf-8'))
await writer.drain() #drain()函数是在事件循环中刷新缓存区,
#也就是把需要发送出去的数据从缓存区发出去
#特别在数据量大情况下保证数据完整性
while True:
line=await reader.readline()
if line==b'\r\n':
break
print('%s header > %s' %(host,line.decode('utf-8').rstrip()))
writer.close()
loop=asyncio.get_event_loop()
tasks=[wget(host) for host in ['www.sina.com.cn','www.sohu.com','www.163.com']]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
运行结果:
Task类和Future类
task类是Future类的子类,task负责协程在事件循环中的执行,如果已包装的协程yield from一个future,则task终止已包装协程的执行,等待future的完成。当future完成后,协程带着future执行的结果或异常重新开始执行。
事件循环使用协作调度:事件循环每次只能运行一个task。其他事件循环在不同线程运行时,其他的task就可以并行运行。当一个task等待future的完成时,事件循环会执行一个新的task。
task的取消不同于future的取消,调用cancel()将向协程抛出CancelledError异常。若已包装的协程没有捕捉到CancelledError异常,cancelled()将只返回True。
Task类是非线程安全的,我们介绍一个他的主要方法。
task实例的创建
有两种方法创建Task实例,分别为ensure_future()函数或者AbstractEventLoop.create_task()方法。
ensure_future函数的定义如下,参数为coroutine或future对象,返回一个task对象。
asyncio.ensure_future(coro_or_future, *, loop=None)
@asyncio.coroutine
def hello():
print("Hello world!")
r=yield from asyncio.sleep(1)
print("Hello again!")
#这是一个coroutine对象,此时该函数并没有执行
coroutine=hello()
#获取eventloop
loop=asyncio.get_event_loop()
#创建task
#task=loop.create_task(coroutine)
task=asyncio.ensure_future(coroutine)
print(task)
#将协程加入到事件循环loop中,执行coroutine
loop.run_until_complete(task)
print(task)
loop.close()
运行结果为:
输出task我们可以看到,在协程执行前,task是处于pending状态,协程执行完毕后,task处于finished状态。我们也可以看到task对应的协程,这正是验证了task是对协程的进一步封装。
Task一些方法的使用
import asyncio
import time
now = lambda: time.time()
async def do_some_work(x):
print("waiting:",x)
await asyncio.sleep(x)
return "Done after {}s".format(x)
async def main():
coroutine1=do_some_work(1)
coroutine2=do_some_work(2)
coroutine3=do_some_work(8)
tasks=[
asyncio.ensure_future(coroutine1),
asyncio.ensure_future(coroutine2),
asyncio.ensure_future(coroutine3)
]
#dones,pending=await asyncio.wait(tasks)
#for task in dones:
# print("Task ret:",task.result())
results=await asyncio.gather(*tasks)
for result in results:
print("Task ret:",result)
start=now()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
print("Time:", now()-start)
运行结果为:
coroutine asyncio.wait(futures, *, loop=None, timeout=None, return_when=ALL_COMPLETED)
wait()方法等待一组Future和coroutine对象的完成,其中coroutine对象将会包装成Task对象。该方法返回两组Future:(done,pending),分别表示以完成和未运行的Future。并且只有所有的future完成后才返回,所以当我们输出pending的长度时会发现其为0。
asyncio.gather(*coros_or_futures, loop=None, return_exceptions=False)
返回所给coroutine对象或future的结果。上述例子中我们获得一个list,遍历取得每个coroutine对象的结果。所有的futures必须共享一个event loop。当例子中的tasks取消后,则里面的三个coroutine对象都将取消,当其中的coroutine对象取消后不会影响tasks。
task的并行执行
前面提到,调用协程时并不会直接运行,而是需要调用run_until_complete()方法将协程注册到事件循环,这其实就是将协程包装成立一个task对象。它保存了协程运行后的状态
import asyncio
async def factorial(name,number):
f=1
for i in range(2,number+1):
print("Task %s: Compute factorial(%s)..."%(name,i))
await asyncio.sleep(5)
f*=i
print("Task %s:factorial(%s)=%s"%(name,number,f))
loop=asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(
factorial("A",2),
factorial("B",3),
factorial("C",4),
))
loop.close()
运行结果为:
绑定回调
import asyncio
async def slow_operation(future):
await asyncio.sleep(1)
future.set_result('Future is done!')
def got_result(future):
print(future.result())
loop.stop()
loop=asyncio.get_event_loop()
future=asyncio.Future()
coroutine=slow_operation(future)
task=asyncio.ensure_future(coroutine)
print(task)
task.add_done_callback(got_result)
print(task)
运行结果为:
使用add_done_callback()方法添加一个回调函数,当future也就是task执行完毕后,运行该回调函数,上述例子的回调函数中通过future获取执行的结果。
协程停止
future对象一共有四种状态:pending、running、done和canceled。
当刚创建对象时,其状态为pending,执行的时候为running,执行完毕后为done,还可以对task进行取消。
cancel()
当对task对象取消时,调用此方法。和Future.cancel()方法不同的是,它不保证task会被取消:异常可能会被捕捉并立刻反应、延迟取消或者不取消。当cancel()被调用后,canceled()方法不一定返回True,只有当coroutine因CancelledError异常而终止的时候返回True。
AbstractEventLoop.run_forever()
运行event loop
AbstractEventLoop.stop()
停止运行事件循环
AbstractEventLoop.is_closed()
当事件循环关闭时返回True
AbstractEventLoop.close()
关闭事件循环。循环将不能运行,挂起的回调将丢失。它将清除队列并且关闭执行器,但不会等待执行器完成。它是不可逆的,在此之后不应该调用其他方法。
Abstracteventloop.run_until_complete(future)
运行直到future对象运行完
参考:
Python官方文档.
python中重要的模块–asyncio.