异步方法介绍(二)

异步方法介绍(二)

上次讲异步的时候讲了一些简单的异步实现,这一次稍微深入一下。

上次使用低级的事件循环来监视任务,其实asyncio模块里面提供了高级的run函数可以直接开始一个事件循环,不用手动调用。不知道小伙伴们注意到上次wait函数返回的对象没有,返回的对象都是Future类实例,这个类就是将来要做的任务,可以通过asyncio.create_task显式地创建一个Future对象,这个对象有很多方法。

源代码如下:

""" 异步的高级操作

使用run函数来操作协程
asyncio.run(async_func(...))

创建任务
t = asyncio.create_task(async_func(...))
await t

the statistics of this file:
lines(count)    understand_level(h/m/l)    classes(count)    functions(count)    fields(count)
000000000092    ----------------------m    00000000000000    0000000000000004    ~~~~~~~~~~~~~
"""

import time
import asyncio

__author__ = '与C同行'


async def wait_noblock_n_second(n, verbose):
    print('进入wait_noblock_n_second函数')
    await asyncio.sleep(n)
    print(f'{verbose}离开wait_noblock_n_second函数')
    return n+1


async def main_block():
    """
    协程没有包装成Future或者Task类,将阻塞
    """
    print('协程开始')
    time_start = time.time()
    task1 = wait_noblock_n_second(1, '1s之后')
    task2 = wait_noblock_n_second(2, '2s之后')
    await task1
    await task2
    time_end = time.time()
    print(f'运行耗时:{time_end-time_start}s')
    print('协程结束')


async def main_noblock():
    """
    协程包装成Future或者Task类,不会阻塞
    """
    print('协程开始')
    time_start = time.time()
    task1 = asyncio.create_task(wait_noblock_n_second(1, '1s之后'))
    task2 = asyncio.create_task(wait_noblock_n_second(2, '2s之后'))
    await task1
    await task2
    time_end = time.time()
    print(f'运行耗时:{time_end-time_start}s')
    print('协程结束')


async def main_noblock_gather():
    """
    使用gather来等待协程完成,并返回有序的结果
    """
    print('协程开始')
    time_start = time.time()
    task1 = asyncio.create_task(wait_noblock_n_second(3, '3s之后'))
    task2 = asyncio.create_task(wait_noblock_n_second(2, '2s之后'))
    task3 = asyncio.create_task(wait_noblock_n_second(1, '1s之后'))
    tasks = [task1, task2, task3]
    results = await asyncio.gather(*tasks)
    time_end = time.time()
    print(f'所有协程运行耗时:{time_end-time_start}s')
    print('各个协程gather之后的结果:')
    for item in results:
        print(item)
    print('协程结束')


if __name__ == '__main__':
    print(f'当前时间:{time.ctime()}')

    print()
    print('使用run函数来运行协程')
    asyncio.run(main_block())
    asyncio.run(main_noblock())
    asyncio.run(main_noblock_gather())

    print()
    print('显式调用轮询器调度协程')
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main_block())
    loop.run_until_complete(main_noblock())
    loop.run_until_complete(main_noblock_gather())
    loop.close()

这一次的异步方法起点和上次不一样了,这一次是从一个异步函数进去的,上次是一次启动所有的异步函数。

先看一下不创建Future对象的异步方法,这里使用run调用一个异步函数,隐式地调用事件循环器:

if __name__ == '__main__':
    print(f'当前时间:{time.ctime()}')

    print()
    print('使用run函数来运行协程')
    asyncio.run(main_block())

结果如下:
在这里插入图片描述
可以看到不创建Future对象,即使使用await语句等待异步函数也会阻塞程序。

使用Future对象:

if __name__ == '__main__':
    print(f'当前时间:{time.ctime()}')

    print()
    print('使用run函数来运行协程')
    asyncio.run(main_noblock())

结果如下:
在这里插入图片描述
使用Future对象之后就不会阻塞程序了。

接下来使用gather来收集任务:

if __name__ == '__main__':
    print(f'当前时间:{time.ctime()}')

    print()
    print('使用run函数来运行协程')
    asyncio.run(main_noblock_gather())

结果如下:
在这里插入图片描述
现在使用显式的事件循环器来监视异步函数:

if __name__ == '__main__':
    print(f'当前时间:{time.ctime()}')

    print()
    print('显式调用轮询器调度协程')
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main_block())
    loop.close()

结果如下:
在这里插入图片描述

if __name__ == '__main__':
    print(f'当前时间:{time.ctime()}')

    print()
    print('显式调用轮询器调度协程')
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main_noblock())
    loop.close()

结果如下:
在这里插入图片描述

if __name__ == '__main__':
    print(f'当前时间:{time.ctime()}')

    print()
    print('显式调用轮询器调度协程')
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main_noblock_gather())
    loop.close()

结果如下:
在这里插入图片描述
使用run运行异步函数与使用事件循环器运行异步函数的结果是一致的。

总结:在异步函数中有多个异步函数调用时要创建任务,否则该异步调用函数将会阻塞程序。

喜欢python的朋友可以关注微信公众号“与C同行”,里面有更多python技巧。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值