通过一个例子分析python3异步编程过程

以下代码来源于:https://www.pythonf.cn/read/99110

import asyncio
import time

now = lambda: time.time()

async def do_some_work(x):
    print('Waiting: ', x) 
    await asyncio.sleep(x) # 执行sleep时,await使此协程主动让出控制权,loop调用其他协程
    return 'Done after {}s'.format(x)

start = now()

coroutine = do_some_work(2)
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(coroutine)
loop.run_until_complete(task)

print('Task ret: ', task.result())
print('TIME: ', now() - start) 

结果:

Waiting:  2
Task ret:  Done after 2s
TIME:  2.0022029876708984

分析一下代码:

async def do_some_work(x):
    print('Waiting: ', x) 
    await asyncio.sleep(x) # 执行sleep时,await使此协程主动让出控制权,loop调用其他协程
    return 'Done after {}s'.format(x)

这里通过async关键字将do_some_work变成了awaitable函数,通过await关键字在此处将协程的控制权交给event loop x秒

coroutine = do_some_work(2)

这里通过调用awaitable函数,返回coroutine对象

>>> type(coroutine)
coroutine
# 由于asyncio.wait也是awaitable函数,所以
>>> type(asyncio.wait(3))
coroutine
loop = asyncio.get_event_loop()

这里通过get_event_loop()方法获取到当前event loop,如果当前是主线程,且set_event_loop()没有调用,则该方法会创建一个event loop并设置为当前event loop
https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.get_event_loop

task = asyncio.ensure_future(coroutine)

这里将coroutine对象转为Task对象,Task是Future的子类,因而可以保存未来协程执行的结果,官方推荐Python 3.7+使用create_task()方法
https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task

loop.run_until_complete(task)

这里将任务注册到event loop中,并运行event loop
如果run_until_complete接收的是coroutine对象,则会自动将其转为Task对象,所以代码可改写为:

coroutine = do_some_work(2)
loop = asyncio.get_event_loop()
loop.run_until_complete(coroutine)

https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_until_complete

由于控制event loop过于复杂,官方更推荐在Python 3.7+中使用asyncio.run()

import asyncio
import time

now = lambda: time.time()

async def do_some_work(x):
    print('Waiting: ', x) 
    await asyncio.sleep(x) # 执行sleep时,await使此协程主动让出控制权,loop调用其他协程
    return 'Done after {}s'.format(x)

start = now()

coroutine = do_some_work(2)
result = asyncio.run(coroutine)

print('Task ret: ', result)
print('TIME: ', now() - start) 

结果:

Waiting:  2
Task ret:  Done after 2s
TIME:  2.0022029876708984

https://docs.python.org/3/library/asyncio-task.html#asyncio.run

如果将get_event_loop改为new_event_loop

loop = asyncio.new_event_loop()

则再次运行代码时会报错:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-1-8bdce8a449ee> in <module>
     14 loop = asyncio.new_event_loop()
     15 task = asyncio.ensure_future(coroutine)
---> 16 loop.run_until_complete(task)
     17
     18 print('Task ret: ', task.result())

/usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/base_events.py in run_until_complete(self, future)
    593
    594         new_task = not futures.isfuture(future)
--> 595         future = tasks.ensure_future(future, loop=self)
    596         if new_task:
    597             # An exception is raised if the future didn't complete, so there

/usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/tasks.py in ensure_future(coro_or_future, loop)
    665     elif futures.isfuture(coro_or_future):
    666         if loop is not None and loop is not futures._get_loop(coro_or_future):
--> 667             raise ValueError('The future belongs to a different loop than '
    668                              'the one specified as the loop argument')
    669         return coro_or_future

ValueError: The future belongs to a different loop than the one specified as the loop argument

这是由于task并没有绑定在loop中
可以采用三种方式修改:
第一种:使用set_event_loop方法将loop绑定在当前线程

coroutine = do_some_work(2)
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
task = asyncio.ensure_future(coroutine)
loop.run_until_complete(task)

第二种:在ensure_future中使用loop参数指定task绑定的loop

coroutine = do_some_work(2)
loop = asyncio.new_event_loop()
task = asyncio.ensure_future(coroutine, loop=loop)
loop.run_until_complete(task)

第三种:使用loop的create_task方法实现将task绑定在该loop上

coroutine = do_some_work(2)
loop = asyncio.new_event_loop()
task = loop.create_task(coroutine)
loop.run_until_complete(task)

感觉写的有点乱,没办法asyncio里的知识点太零碎了,而且比较抽象,要理解它需要理解里面的几个关键知识点:

  • event loop
  • future
  • coroutine
  • async/await

关于asyncio.run
Python 3.7开始作为暂定引入该API,Python 3.8稳定,该API用来自动处理事件循环以简化代码,以下两段代码来自官方文档

import asyncio

async def main():
    await asyncio.sleep(0)
    return 42

asyncio.run(main())

等价:

import asyncio

async def main():
    await asyncio.sleep(0)
    return 42

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
    loop.run_until_complete(main())
finally:
    asyncio.set_event_loop(None)
    loop.close()
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值