asyncio:异步I/O、事件循环和并发工具(持续跟新中)

流畅的Python书中的协程部分版本太低,而且讲的比较少,这次根据Python3标准库书中实例来学习记录asyncio的使用。

asyncio模块提供了使用次饿成构建并发应用的工具。threading模块通过应用线程并发,mutilprocessing使用系统进程实现并发,asyncio则使用一个单线程单进程的方法来实现并发,应用的各个部分会彼此合作,在最优的时刻显式地切换任务。

asyncio提供的框架以一个事件循环(event loop)为中心,这是一个首类对象,负责高效地处理I/O事件、系统事件、和应用的上下文切换。

就现在本人的了解,一般的io应用中,还是以多线程使用为主,自己在写协程并发的时候,多个协程之间,无法有效的设置条件交出控制权。

唯一能应用的包,就一个aiohttp,如果我想用另外的包实现协程,基本无法做到,然而协程asyncio包就像流畅的Python书中所说,大部分在讲概念和API,

也只能希望后面能有更加丰富的包来配合协程,或者等我哪一天成为高手,写出能配合协程的包。

2、利用协程合作完成多任务

import asyncio


async def coroutine():
    print('in coroutine')

# 定义事件循环
event_loop = asyncio.get_event_loop()

try:
    print('starting coroutine')
    coro = coroutine()
    print('entering event loop')
    # 运行协程
    event_loop.run_until_complete(coro)
finally:
    print('closing event loop')
    event_loop.close()
/usr/local/bin/python3.7 /Users/shijianzhong/study/t_asyncio/asyncio_coroutine.py
starting coroutine
entering event loop
in coroutine
closing event loop

Process finished with exit code 0

 先通过get_event_loop创建一个默认的事件循环,run_until_complete方法驱动里面的协程,最后关闭事件循环。

从协程返回值

import asyncio


# 有返回值
async def coroutine():
    print('in coroutine')
    return 'result'

# 定义事件循环
event_loop = asyncio.get_event_loop()

try:
    print('starting coroutine')
    coro = coroutine()
    print('entering event loop')
    # 运行协程,获取值
    return_value = event_loop.run_until_complete(coro)
    print(f'it returned: {return_value!r}')
finally:
    print('closing event loop')
    event_loop.close()
/usr/local/bin/python3.7 /Users/shijianzhong/study/t_asyncio/asyncio_coroutine_return.py
starting coroutine
entering event loop
in coroutine
it returned: 'result'
closing event loop

Process finished with exit code 0

串链协程

import asyncio


async def outer():
    print('in outer')
    print('waiting for result1')
    # 接收phase1()协程产生的值
    result1 = await phase1()
    print('waiting for result2')
    # 接收phase2()协程产生的值
    result2 = await phase2(result1)
    return result1, result2


async def phase1():
    print('in parse1')
    return 'result1'

async def phase2(arg):
    print('in phase2')
    return 'result2 derived from {}'.format(arg)


event_loop = asyncio.get_event_loop()
try:
    return_value = event_loop.run_until_complete(outer())
    print(f'return_value:{return_value!r}')
finally:
    event_loop.close()
/usr/local/bin/python3.7 /Users/shijianzhong/study/t_asyncio/asyncio_coroutine_chain.py
in outer
waiting for result1
in parse1
waiting for result2
in phase2
return_value:('result1', 'result2 derived from result1')

Process finished with exit code 0

生成器而不是协程

Python3.5开始

async 代替了@asyncio.coroutine

await 代替了 yield from

本人觉得await 还是yield from更加直观。

3、调度常规函数调用

这个使用的话,需要把事件循环传递放入协程中,协程中通过事件循环的方法去激活需要调用的函数。

在传入事件循环的协程里面必须设置asyncio.seleep,要不然协程不会让出控制权,事件循环根本无法激活调用函数。

import asyncio
from functools import partial


def callback(arg, *, kwarg='default'):
    print(f'callback invoked with {arg} and {kwarg}')


async def main(loop):
    print('registering callbacks')
    # 调用函数,只能传入一个参数,多参数传入调用partial
    loop.call_soon(callback, 1)
    # 通过partial传入关键字参数
    wrapped = partial(callback, kwarg='not default')
    loop.call_soon(wrapped, 2)

    await asyncio.sleep(.1)


event_loop = asyncio.get_event_loop()
try:
    print('entering event loop')
    event_loop.run_until_complete(main(event_loop))
finally:
    event_loop.close()
/usr/local/bin/python3.7 /Users/shijianzhong/study/t_asyncio/asyncio_call_soon.py
entering event loop
registering callbacks
callback invoked with 1 and default
callback invoked with 2 and not default

Process finished with exit code 0

用Delay调度回调

call_soon是直接调用,call_later()第一个参数可以传入延后的事件,单位为秒,第二个参数为function

在传入事件循环的协程里面必须设置asyncio.seleep,且大于最迟的调用函数时间,要不然协程不会让出控制权,事件循环根本无法激活调用函数。

import asyncio
from functools import partial


def callback(arg):
    print(f'callback invoked {arg}')


async def main(loop):
    print('registering callbacks')
    # 调用函数,第一个参数传入时间
    loop.call_later(0.2, callback, 1)
    loop.call_later(0.1, callback, 2)
    # 这个很重要
    await asyncio.sleep(.21)


event_loop = asyncio.get_event_loop()
try:
    print('entering event loop')
    event_loop.run_until_complete(main(event_loop))
finally:
    print('closing event loop')
    event_loop.close()
/usr/local/bin/python3.7 /Users/shijianzhong/study/t_asyncio/asyncio_call_later.py
entering event loop
registering callbacks
callback invoked 2
callback invoked 1
closing event loop

Process finished with exit code 0

在指定事件内调度一个回调

实现这个目的的循环依赖的是一个所谓的单调时钟,用loop.time()生成,激活用call_at

import asyncio
import time


def callback(n, loop):
    print(f'callback {n} invoked at {loop.time()}')


async def main(loop):
    # 运行结果来看,这now是从0开始的
    now = loop.time()
    print(f'clock time: {time.time()}')
    print(f'loop  time {now}')
    print('registering callbacks')
    # 加0.2秒
    loop.call_at(now + .2, callback, 1, loop)
    # 加0.1 秒
    loop.call_at(now + .1, callback, 2, loop)
    # 马上开始
    loop.call_soon(callback, 3, loop)

    await asyncio.sleep(1)


event_loop = asyncio.get_event_loop()
try:
    print('entering event loop')
    event_loop.run_until_complete(main(event_loop))
finally:
    print('closing event loop')
    event_loop.close()
/usr/local/bin/python3.7 /Users/shijianzhong/study/t_asyncio/asynvio_call_at.py
entering event loop
clock time: 1579366496.336677
loop  time 0.086557153
registering callbacks
callback 3 invoked at 0.086701756
callback 2 invoked at 0.189241196
callback 1 invoked at 0.29037571
closing event loop

Process finished with exit code 0

猜你喜欢

转载自www.cnblogs.com/sidianok/p/12210857.html