asyncio入门教程

1.asyncio异步并发概念
asyncio 显示的处理上下文的切换,以事件循环(event loop)为中心,程序开启一个无限的循环,程序会把一些函数注册到事件循环上,当满足事件发生的时候,调用相应的协程函数
2.写一个异步方法

async def async_double(x):
	return 2*x

从外观上看异步方法和标准方法没有什么区别,只是前面多了个async,要调用异步函数,必须使用await关键字,不能在同步函数里面使用await,否则会报错

async def print_double(x):
	print(await async_double(x))

3.调用协程函数

import asyncio 
async def foo():
	print("这是一个协程")
if  __name__=='__main__':
	loop = asyncio.get_event_loop()
	try:
		coro = foo()
		print("进入事件循环")
		loop.run_until_complete(coro)
	finally:
		print("关闭事件循环")
		loop.close()

输出

开始运行协程
进入事件循环
这是一个协程
关闭事件循环

4.获取协程中的返回值

import asyncio 
async def foo():
	print("这是一个协程")
	return "返回值"
if __name__=="__main__":
	loop = asyncio.get_event_loop()
	try:
		print("开始运行协程")
		coro = foo()
		print("进入事件循环")
		result =loop.run_until_complete(coro)
		print(r"run_until_complete 可以获取协程的{result} ,默认输出None")
	finally:
		print("关闭事件循环")
		loop.close()

run_until_comlete 可以获取协程的返回值,如果没有给定返回值,则像函数一样,默认返回None
5.协程调用协程
在协程中,可以启动另一个协程,我们可以在协程中使用await关键字,链式的调度协程,来形成一个协程任务流。

import asyncio 
async def main():
	print("主协程")
	print("等待result1协程运行")
	res1 = await result1()
	print("等待result2协程运行")
	res2 = await reuslt2(res1)
	return (res1,res2)
async def result1():
	print("这是result1协程")
	return "result1"
async def result2(arg):
	print("这是result2的协程")
	return f"result2 接收了一个参数{arg}"
if  __name__=="__main__":
	loop = asyncio.get_event_loop()
	try:
		result = loop.run_until_complete(main())
		print(f"获取返回值:{result}")
	finally:
		print("关闭事件循环")
		loop.close()

输出

主协程
等待result1协程运行
这是result1协程
等待result2协程运行
这是result2协程
获取返回值:('result1', 'result2接收了一个参数,result1')
关闭事件循环

6.协程调用普通函数
可以使用的关键字有call_soon,call_later,call_at,call_soon
call_soon 作用是调用后立即返回

loop.call_soon(callback,*args,context=None)

call_soon 支持位置参数,但不支持关键字参数,如果要支持关键字参数,要使用functools.aprtial()对方法进一步封装,可以选关键字context 允许指定要运行的回调的自定义contextvars.Context.当没有提供上下文时使用当前上下文

import asyncio 
import functools 
def callback(args,*,kwargs="default"):
	print(f"普通函数做为回调函数,获取参数:{args},{kwargs}")
async def main(loop):
	print("注册callback")
	loop.call_soon(callback,1)
	wrapped = functools.partial(callback,kwargs="not default")
	loop.call_soon(wrapped,2)
	await asyncio.sleep(0.2)
if  __name__=="__main__":
	loop = ayncio.get_event_loop()
	try:
		loop.run_until_comlete(main(loop))
	finally:
		loop.close()

输出结果

注册callback
普通函数做为回调函数,获取参数:1,defalut
普通函数做为回调函数,获取参数:2,not defalut

7.future
future 表示还没有完成的工作结果,事件循环可以通过监视一个future 对象的状态来指示它已经完成。future对象有几个状态
Pending
Running
Done
Cancelled
创建future 的时候,task为pending ,事件循环调用执行的时候当然是running,调用完毕自然就是done,如果需要停止事件循环,就需要先把task取消,状态为cancel

import asyncio 
def foo(future,result):
	print(f"此时的future的状态,{future}")
	print(f"设置future的结果:{result}")
    future.set_result(result)
    print(f"此时future的状态:{future}")
 if __name__== "__main__":
 	loop = asyncio.get_event_loop()
 	try:
 		all_done = asyncio.Future()
 		loop.call_soon(foo,all_done,"Future is done")
 		print("进入事件循环")
 		result = loop.run_until_complete(all_done)
 	finally:
 		print("关闭事件循环")
 		loop.close()
 	print("获取future的结果",all_done.result())

输出

进入事件循环
此时future的状态:<Future pending cb=[_run_until_complete_cb() at /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/base_events.py:176]>
设置future的结果:Future is done!
此时future的状态:<Future finished result='Future is done!'>
返回结果 Future is done!
关闭事件循环
获取future的结果 Future is done!

future 的实例all_done 会保留提供的方法结果,可以在后续使用,set_result 之后对象的状态有pending变为了finished
8.future 对象使用await

import asyncio 
def foo(future,result):
	print("设置结果到future",result)
	future.set_result(result)
async def main(loop):
	all_done = ayncio.Future()
	print("调用函数获取future对象")
	loop.call_soon(foo,all_done,"the result")
	result = await all_done
	print("获取future 里的结果",result)
if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    try:
        loop.run_until_complete(main(loop))
    finally:
        loop.close()

9.Future 回调
Future 在完成的时候可以执行一些回调函数,回调函数按注册时的顺序进行调用

import asyncio 
import functools 
def callback(future,n):
	print(n,future.result())
async def register_callbacks(all_done):
    print('注册callback到future对象')
    all_done.add_done_callback(functools.partial(callback, n=1))
    all_done.add_done_callback(functools.partial(callback, n=2))


async def main(all_done):
    await register_callbacks(all_done)
    print('设置future的结果')
    all_done.set_result('the result')

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    try:
        all_done = asyncio.Future()
        loop.run_until_complete(main(all_done))
    finally:
        loop.close()

通过add_done_callback 方法给funtrue 任务添加回调函数,当future执行完成的时候,就会调用回调函数,并通过参数future 获取协程执行的结果
到此为止,我们就学会了如何在协程中调用一个普通函数并获取其结果。
10.并发的执行任务
Task 任务是Future 子类,使用方法和future一样,协程可以等待任务,每个任务都有一个结果,在它完成后可以获取到这个结果,因为协程没有状态,我们通过create_task()方法可以将协程包装成有状态的任务,可以在任务运行的过程中取消任务

import asyncio 
async def  child():
	print("进入子协程")
	return "the result"
async def main(loop):
	print("将协程child  包装成任务")
	task = loop.create_task(child())
	print("通过cancel方法可以取消任务")
	task.cancel()
	try:
		await task 
	except asyncio.CancelledError:
		print("取消任务抛出CancelledError异常")
	else:
		print("获取任务的结果",task.result())
if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    try:
        loop.run_until_complete(main(loop))
    finally:
        loop.close()

输出

将协程child包装成任务
通过cancel方法可以取消任务
取消任务抛出CancelledError异常

如果我们把上面的task.canncel ()注释掉我们可以得到正常情况下的结果

将协程child包装成任务
通过cancel方法可以取消任务
进入子协程
获取任务的结果 the result

除了可以使用loop.create_task 将协程包装为任务外,还可以使用ayncio.ensure_future(coroutine)建一个task
11 组合协程
等待多个协程

import asyncio


async def num(n):
    try:
        await asyncio.sleep(n*0.1)
        return n
    except asyncio.CancelledError:
        print(f"数字{n}被取消")
        raise


async def main():
    tasks = [num(i) for i in range(10)]
    complete, pending = await asyncio.wait(tasks, timeout=0.5)
    for i in complete:
        print("当前数字",i.result())
    if pending:
        print("取消未完成的任务")
        for p in pending:
            p.cancel()


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    try:
        loop.run_until_complete(main())
    finally:
        loop.close()

输出

当前数字 1
当前数字 2
当前数字 0
当前数字 4
当前数字 3
取消未完成的任务
数字5被取消
数字9被取消
数字6被取消
数字8被取消
数字7被取消

内部wait()使用set保存它创建的task实例,因为set是无序所以我们的任务不是按顺序执行,wait返回的是一个元祖,包括两个集合,分别表示已完成和未完成的任务,wait第二个参数为一个超时值,达到这个超时时间后,未完成的任务状态变为pending,当程序退出时还有任务没有完成此时就会看到如下的错误提示。

Task was destroyed but it is pending!
task: <Task pending coro=<num() done, defined at 11.py:12> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x1093e0558>()]>>
Task was destroyed but it is pending!
task: <Task pending coro=<num() done, defined at 11.py:12> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x1093e06d8>()]>>
Task was destroyed but it is pending!
task: <Task pending coro=<num() done, defined at 11.py:12> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x1093e0738>()]>>

我们可以用迭代调用cancel方法取消任务

if pending:
        print("取消未完成的任务")
        for p in pending:
            p.cancel()

gather 的使用
gather的作用和wait类似不同的是。
1.gather任务无法取消。
2.返回值是一个结果列表
3.可以按照传入参数的顺序,顺序输出。
我们将上面的代码改为gather的方式

import asyncio


async def num(n):
    try:
        await asyncio.sleep(n * 0.1)
        return n
    except asyncio.CancelledError:
        print(f"数字{n}被取消")
        raise


async def main():
    tasks = [num(i) for i in range(10)]
    complete = await asyncio.gather(*tasks)
    for i in complete:
        print("当前数字", i)


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    try:
        loop.run_until_complete(main())
    finally:
        loop.close()

输出

当前数字 0
当前数字 1
....中间部分省略
当前数字 9

gather通常被用来阶段性的一个操作,做完第一步才能做第二步,比如下面这样

import asyncio

import time


async def step1(n, start):
    await asyncio.sleep(n)
    print("第一阶段完成")
    print("此时用时", time.time() - start)
    return n


async def step2(n, start):
    await asyncio.sleep(n)
    print("第二阶段完成")
    print("此时用时", time.time() - start)
    return n


async def main():
    now = time.time()
    result = await asyncio.gather(step1(5, now), step2(2, now))
    for i in result:
        print(i)
    print("总用时", time.time() - now)


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    try:
        loop.run_until_complete(main())
    finally:
        loop.close()

输出

第二阶段完成
此时用时 2.0014898777008057
第一阶段完成
此时用时 5.002960920333862
5
2
总用时 5.003103017807007

可以通过上面结果得到如下结论:
1.step1和step2是并行运行的。
2.gather会等待最耗时的那个完成之后才返回结果,耗时总时间取决于其中任务最长时间的那个。
任务完成时进行处理
as_complete是一个生成器,会管理指定的一个任务列表,并生成他们的结果。每个协程结束运行时一次生成一个结果。与wait一样,as_complete不能保证顺序,不过执行其他动作之前没有必要等待所以后台操作完成。

import asyncio
import time


async def foo(n):
    print('Waiting: ', n)
    await asyncio.sleep(n)
    return n


async def main():
    coroutine1 = foo(1)
    coroutine2 = foo(2)
    coroutine3 = foo(4)

    tasks = [
        asyncio.ensure_future(coroutine1),
        asyncio.ensure_future(coroutine2),
        asyncio.ensure_future(coroutine3)
    ]
    for task in asyncio.as_completed(tasks):
        result = await task
        print('Task ret: {}'.format(result))


now = lambda : time.time()
start = now()

loop = asyncio.get_event_loop()
done = loop.run_until_complete(main())
print(now() - start)

输出

Waiting:  1
Waiting:  2
Waiting:  4
Task ret: 1
Task ret: 2
Task ret: 4
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值