Python中的asyncio代码详解
asyncio介绍
熟悉c#的同学可能知道,在c#中可以很方便的使用 async和 await来实现异步编程,那么在python中应该怎么做呢,其实python也支持异步编程,一般使用 asyncio这个库,下面介绍下什么是 asyncio:
asyncio是用来编写并发代码的库,使用 async/await语法。 asyncio被用作多个提供高性能 Python异步框架的基础,包括网络和网站服务,数据库连接库,分布式任务队列等等。 asyncio往往是构建IO密集型和高层级结构化网络代码的最佳选择。
asyncio中的基本概念
可以看见,使用asyncio库我们也可以在python代码中使用 async和 await。在 asyncio中,有四个基本概念,分别是:
Eventloop
Eventloop可以说是 asyncio应用的核心,中央总控, Eventloop实例提供了注册、取消、执行任务和回调的方法。简单来说,就是我们可以把一些异步函数注册到这个事件循环上,事件循环回循环执行这些函数(每次只能执行一个),如果当前正在执行的函数在等待I/O返回,那么事件循环就会暂停它的执行去执行其他函数。当某个函数完成I/O后会恢复,等到下次循环到它的时候就会继续执行。
Coroutine
协程本质就是一个函数,
importasyncio importtime asyncdefa(): print('Suspendinga') awaitasyncio.sleep(3) print('Resuminga') asyncdefb(): print('Suspendingb') awaitasyncio.sleep(1) print('Resumingb') asyncdefmain(): start=time.perf_counter() awaitasyncio.gather(a(),b()) print(f'{main.__name__}Cost:{time.perf_counter()-start}') if__name__=='__main__': asyncio.run(main())
执行上述代码,可以看到类似这样的输出:
Suspendinga
Suspendingb
Resumingb
Resuminga
mainCost:3.0023356619999997
关于协程的具体介绍,可以参考我以前的文章python中的协程不过以前的那种写法,需要使用装饰器,已经过时了。
Future
Future是表示一个“未来”对象,类似于 javascript中的 promise,当异步操作结束后会把最终结果设置到这个 Future对象上, Future是对协程的封装。
>>>importasyncio >>>deffun(): ...print("innerfun") ...return111 ... >>>loop=asyncio.get_event_loop() >>>future=loop.run_in_executor(None,fun)#这里没有使用await innerfun >>>future#可以看到,fun方法状态是pending._call_check_cancel()at/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/futures.py:348]> >>>future.done()#还没有完成 False >>>[mformindir(future)ifnotm.startswith('_')] ['add_done_callback','cancel','cancelled','done','exception','get_loop','remove_done_callback','result','set_exception','set_result'] >>>future.result()#这个时候如果直接调用result()方法会报错 Traceback(mostrecentcalllast): File"",line1,in asyncio.base_futures.InvalidStateError:Resultisnotset. >>>asyncdefrunfun(): ...result=awaitfuture ...print(result) ... >>>loop.run_until_complete(runfun())#也可以通过loop.run_until_complete(future)来执行,这里只是为了演示await 111 >>>future >>>future.done() True >>>future.result() 111 Task
Eventloop除了支持协程,还支持注册 Future和 Task2种类型的对象,而 Future是协程的封装, Future对象提供了很多任务方法(如完成后的回调,取消,设置任务结果等等),但是一般情况下开发者不需要操作 Future这种底层对象,而是直接用 Future的子类 Task协同的调度协程来实现并发。那么什么是 Task呢?下面介绍下:
一个与 Future类似的对象,可运行 Python协程。非线程安全。 Task对象被用来在事件循环中运行协程。如果一个协程在等待一个 Future对象, Task对象会挂起该协程的执行并等待该 Future对象完成。当该 Future对象完成被打包的协程将恢复执行。事件循环使用协同日程调度:一个事件循环每次运行一个 Task对象。而一个 Task对象会等待一个 Future对象完成,该事件循环会运行其他 Task、回调或执行IO操作。
下面看看用法:
>>>asyncdefa(): ...print('Suspendinga') ...awaitasyncio.sleep(3) ...print('Resuminga') ... >>>task=asyncio.ensure_future(a()) >>>loop.run_until_complete(task) Suspendinga Resuminga
asyncio中一些常见用法的区别
Asyncio.gather和asyncio.wait
我们在上面的代码中用到过 asyncio.gather,其实还有另外一种用法是 asyncio.wait,他们都可以让多个协程并发执行,那么他们有什么区别呢?下面介绍下。
>>>importasyncio >>>asyncdefa(): ...print('Suspendinga') ...awaitasyncio.sleep(3) ...print('Resuminga') ...return'A' ... ... ...asyncdefb(): ...print('Suspendingb') ...awaitasyncio.sleep(1) ...print('Resumingb') ...return'B' ... >>>asyncdeffun1(): ...return_value_a,return_value_b=awaitasyncio.gather(a(),b()) ...print(return_value_a,return_value_b) ... >>>asyncio.run(fun1()) Suspendinga Suspendingb Resumingb Resuminga AB >>>asyncdeffun2(): ...done,pending=awaitasyncio.wait([a(),b()]) ...print(done) ...print(pending) ...task=list(done)[0] ...print(task) ...print(task.result()) ... >>>asyncio.run(fun2()) Suspendingb Suspendinga Resumingb Resuminga {:1>result='A'>, :8>result='B'>} set() :1>result='A'> A
根据上述代码,我们可以看出两者的区别:
asyncio.gather能收集协程的结果,而且会按照输入协程的顺序保存对应协程的执行结果,而 asyncio.wait的返回值有两项,第一项是完成的任务列表,第二项表示等待完成的任务列表。
asyncio.wait支持接受一个参数 return_when,在默认情况下, asyncio.wait会等待全部任务完成 (return_when='ALL_COMPLETED'),它还支持 FIRST_COMPLETED(第一个协程完成就返回)和 FIRST_EXCEPTION(出现第一个异常就返回):
>>>asyncdeffun2(): ...done,pending=awaitasyncio.wait([a(),b()],return_when=asyncio.tasks.FIRST_COMPLETED) ...print(done) ...print(pending) ...task=list(done)[0] ...print(task) ...print(task.result()) ... >>>asyncio.run(fun2()) Suspendinga Suspendingb Resumingb {:8>result='B'>} { :3>wait_for= ()]>>} :8>result='B'> B
一般情况下,用 asyncio.gather就足够了。
asyncio.create_task和loop.create_task以及asyncio.ensure_future
这三种方法都可以创建 Task,从Python3.7开始可以统一的使用更高阶的 asyncio.create_task.其实 asyncio.create_task就是用的 loop.create_task. loop.create_task接受的参数需要是一个协程,但是 asyncio.ensure_future除了接受协程,还可以是 Future对象或者 awaitable对象:
- 如果参数是协程,其底层使用 loop.create_task,返回 Task对象
- 如果是 Future对象会直接返回
- 如果是一个 awaitable对象,会 await这个对象的 __await__方法,再执行一次 ensure_future,最后返回 Task或者 Future。
所以 ensure_future方法主要就是确保这是一个 Future对象,一般情况下直接用 asyncio.create_task就可以了。
注册回调和执行同步代码
可以使用 add_done_callback来添加成功回调:
defcallback(future): print(f'Result:{future.result()}') defcallback2(future,n): print(f'Result:{future.result()},N:{n}') asyncdeffuna(): awaitasyncio.sleep(1) return"funa" asyncdefmain(): task=asyncio.create_task(funa()) task.add_done_callback(callback) awaittask #这样可以为callback传递参数 task=asyncio.create_task(funa()) task.add_done_callback(functools.partial(callback2,n=1)) awaittask if__name__=='__main__': asyncio.run(main())
执行同步代码
如果有同步逻辑,想要用 asyncio来实现并发,那么需要怎么做呢?下面看看:
defa1(): time.sleep(1) return"A" asyncdefb1(): awaitasyncio.sleep(1) return"B" asyncdefmain(): loop=asyncio.get_running_loop() awaitasyncio.gather(loop.run_in_executor(None,a1),b1()) if__name__=='__main__': start=time.perf_counter() asyncio.run(main()) print(f'mainmethodCost:{time.perf_counter()-start}') #输出:mainmethodCost:1.0050589740000002
可以使用 run_into_executor来将同步函数逻辑转化成一个协程,第一个参数是要传递 concurrent.futures.Executor实例的,传递 None会选择默认的 executor。
总结
以上所述是小编给大家介绍的Python中的asyncio代码详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!