Python异步IO:asyncio模块

协程

一个协程可以:

  • result=await future 或者 result=yield from future——这两句将会挂起协程,直到future完成后再返回future的运行结果,或者抛出异常。
  • result=await coroutine 或者 result=yield from coroutine——它将等待另一个协程产生结果,或者抛出异常

@asyncio.coroutine——标记基于生成器协程的装饰器,它允许生成器使用yield from来调用async def协程;也允许生成器被async def协程调用,比如使用await表达式。

调用协程后并不会马上执行代码,需要你调度执行。有两种方法可以让其开始运行:从其他协程中调用yield from coroutine 或 await coroutine;使用ensure_future()函数或者AbstractEventLoop.create_task()方法执行。

asyncio一些关键字的说明:

  • event_loop事件循环:程序开启一个无限循环,把一些函数注册到事件循环上,当满足事件发生的时候,调用相应的协程函数。
  • coroutine:协程对象,指一个使用async关键字定义或者@asyncio.coroutine装饰的函数,它的调用不会立刻执行函数,而是会返回一个协程对象。协程对象需注册到事件循环,由事件循环调用。
  • task任务:一个协程对象是一个原生可挂起的函数,任务则是对协程进一步封装,其中包含任务的各种状态。
  • future:代表将来执行或没有执行的任务的结果。与task没有本质上的区别。

使用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类是非线程安全的,我们介绍一个他的主要方法。

  • all_tasks(loop=None):返回事件循环中的所有task。
  • current_task(loop=None):返回事件循环中当前运行的task
  • cancel():task取消,与Future.cancel()不同的是,它不保证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.

猜你喜欢

转载自blog.csdn.net/dxk_093812/article/details/84101817
今日推荐