【异步编程】(二)上手asyncio

事件循环

理解成一个死循环,去检测并执行某些代码
在这里插入图片描述
协程函数和协程对象

比如:async def func():这样定义的一个函数就是协程函数

执行协程函数得到的对象叫做协程对象:result = func(),注意这样执行只会生成对象,而不会执行func()函数的内部代码

那么怎样才能执行它的内部代码呢,这就需要把协程对象交给事件循环去执行:

import asyncio

async def func():
    print(1)

result = func()

loop = asyncio.get_event_loop()
loop.run_until_complete(result)

在python3.7之后提供了一个更加简单的事件循环的代码:

import asyncio

async def func():
    print(1)

result = func()

asyncio.run(result)  # 代替了原来的两行loop代码

await关键字

await+可等待的对象(协程对象、Future对象、Task对象->IO等待)

示例:

import asyncio

async def others():
    print('start')
    await asyncio.sleep(2)
    print('end')
    return '返回值'

async def func():
    print('执行协程函数内部代码:')
    response = await others()
    print('IO请求结束,结果为:', response)

asyncio.run(func())

task对象

task对象的作用:立即将一个任务放入事件循环

示例:

import asyncio

async def func():
    print(1)
    await asyncio.sleep(2)
    print(2)
    return 'return value'

async def main():
    print('start main')
    task1 = asyncio.create_task(func())
    task2 = asyncio.create_task(func())
    print('end main')

    ret1 = await task1
    ret2 = await task2
    print(ret1, ret2)

asyncio.run(main())

对于上面这段代码,首先我们执行main函数,打印start main,然后添加了两个task对象,这两个task对象都将func任务加入事件循环,但是并没有执行。打印完end main之后,遇到了await关键字,await是等待相应的协程执行完并获取结果。task1在执行时遇到了io操作阻塞两秒,自动切换到事件循环中的task2执行。最后的返回结果是:
在这里插入图片描述
上面的代码我们做一下简化:

import asyncio

async def func():
    print(1)
    await asyncio.sleep(2)
    print(2)
    return 'return value'

async def main():
    print('start main')
    task_list = [
        asyncio.create_task(func()),
        asyncio.create_task(func())
    ]
    print('end main')

    done, pending = await asyncio.wait(task_list, timeout=None)
    print(done)

asyncio.run(main())

或者:

import asyncio

async def func():
    print(1)
    await asyncio.sleep(2)
    print(2)
    return 'return value'

task_list = [
    func(),
    func()
]

done, pending = asyncio.run(asyncio.wait(task_list))
print(done)

task对象和future对象都可以用来查看事件的执行状态(pending和finished),不过task需要依赖loop创建,future则不需要
在这里插入图片描述
uvloop:提高asyncio事件循环效率
在这里插入图片描述
绑定回调
在这里插入图片描述
学完之后,我有一个疑问,就是await在什么时候使用?我是这样理解的,要在所有发生IO或者网络IO等耗时操作的代码前面加上await表示线程挂起并执行事件循环中的其他任务。先就按这个理解写在这里,之后如果发现不对再回来改。

asyncio的正确使用姿势

我们来整理一下asyncio实现并发的两种错误姿势和六种正确姿势:

import asyncio
import time

start = time.time()

async def f1():
    print(1)
    await asyncio.sleep(2)
    print(2)

async def f2():
    print(3)
    await  asyncio.sleep(1)
    print(4)

# 第一种错误用法
# async def main():
#     await f1()
#     await f2()

# 第一种正确用法
# async def main():
#     await asyncio.gather(f1(), f2())

# 第二种正确用法
# async def main():
#     await asyncio.wait([f1(), f2()])

# 第三种正确用法
# async def main():
#     task1 = asyncio.create_task(f1())
#     task2 = asyncio.create_task(f2())
#     await task1
#     await task2

# 第四种正确用法,记住await task要写在后面,因为task相当于立即在事件循环中加入任务
# async def main():
#     task1 = asyncio.create_task(f2())
#     await f1()
#     await task1

# 第二种错误用法, 不能这样简写
# async def main():
#     await asyncio.create_task(f1())
#     await asyncio.create_task(f2())

# 第五种正确用法
# async def main():
#     task1 = asyncio.ensure_future(f1())
#     await f2()
#     await task1

# 第六种正确用法
async def main():
    loop = asyncio.get_event_loop()
    task1 = loop.create_task(f1())
    await f2()
    await task1

if __name__ == '__main__':
    asyncio.run(main())
    print('time:', time.time()-start)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值