Python Asyncio 学习笔记

asyncio是什么(什么情况下需要使用asyncio)
同步代码(synchrnous code)我们都很熟悉,就是运行完一个步骤再运行下一个。要在同步代码里面实现"同时"运行多个任务,最简单也是最直观地方式就是运行多个 threads 或者多个 processes。这个层次的『同时运行』多个任务,是操作系统协助完成的。
异步编程(asynchrnous code)与之不同的是,只使用一个进程,不使用 threads,但是也能实现"同时"运行多个任务(这里的任务其实就是函数)。
异步函数必要的可以暂停,把运行的权利交给其他函数。等到时机恰当,又可以恢复之前的状态继续运行。
可交给 asyncio 执行的任务,称为协程(coroutine)。一个协程可以放弃执行,把机会让给其它协程(即 yield from 或 await)。

异步编程的实现
asyncio提供的框架以事件循环(event loop)为中心,程序开启一个无限的循环,程序会把一些函数注册到事件循环上。当满足事件发生的时候,调用相应的协程函数。

几个核心概念
事件循环:一种等待程序分配事件或消息的编程架构
future是一个数据结构,表示还未完成的工作结果。事件循环可以监视Future对象是否完成。
task是Future的一个子类,它知道如何包装和管理一个协程的执行。
异步方法(异步函数,协程(Coroutine))

async def async_double(x):
    return 2 * x

要调用异步函数,必须使用await关键字。

async def print_double(x):	# 同步函数不能加async 
    print(await async_double(x))   #

最简功能代码

import asyncio

async def foo():
    print("这是一个协程")

if __name__ == '__main__':
    loop = asyncio.get_event_loop()	# 定义loop,事件循环的应用
    try:
        print("开始运行协程")
        coro = foo()
        print("进入事件循环")
        loop.run_until_complete(coro)	# 默认循环run_until_complete(coro)方法用这个协程启动循环
        # 协程返回时这个方法将停止循环。
    finally:
        print("关闭事件循环")
        loop.close()

run_until_complete的参数是一个futrue对象。当传入一个协程,其内部会自动封装成task,其中task是Future的子类。

协程调用协程

import asyncio


async def main():
    print("主协程")
    print("等待result1协程运行")
    res1 = await result1()	# result1执行完了才会运行result2
    print("等待result2协程运行")
    res2 = await result2(res1)
    return (res1,res2)


async def result1():
    print("这是result1协程")
    return "result1"


async def result2(arg):
    print("这是result2协程")
    return f"result2接收了一个参数,{arg}"


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    try:
        result = loop.run_until_complete(main())
        print(f"获取返回值:{result}")
    finally:
        print("关闭事件循环")
        loop.close()

协程中调用普通函数

在协程中可以通过一些方法去调用普通的函数。可以使用的关键字有call_soon,call_later,call_at。

import asyncio
import functools


def callback(args, *, kwargs="defalut"):
    print(f"普通函数做为回调函数,获取参数:{args},{kwargs}")
def call_back_at(n, loop):
    print(f"callback {n} 运行时间点{loop.time()}")

async def fun1():
    print('before sleep')
    await asyncio.sleep(5)
    print('after sleep')
async def fun2():
    print('before fun2 sleep')
    await asyncio.sleep(5)
    print('after fun2 sleep')

async def main(loop):
	print("注册callback")
    now = loop.time()
    print('当前时间', now)
    loop.call_soon(callback, 3)         # 协程后最先执行
    loop.call_later(3, callback, 2)     # 先写后执行
    loop.call_later(0.1, callback, 1)     #  后写先执行,只延迟1s
    loop.call_at(now, call_back_at, 1, loop)
    await fun1()
    await fun2()        # 协程会阻塞后面调用函数
    # 基于callback重新定义wrapped函数(参数不同)
    wrapped = functools.partial(callback, kwargs="not defalut")
    loop.call_soon(wrapped, 2)
    loop.call_at(now, call_back_at, 2, loop)    # now已过期,也排call_soon后面
    await asyncio.sleep(0.2)


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(main(loop))
finally:
    loop.close()

Future
获取Futrue里的结果
future表示还没有完成的工作结果。事件循环可以通过监视一个future对象的状态来指示它已经完成。future对象有几个状态:

Pending
Running
Done
Cancelled
创建future的时候,task为pending,事件循环调用执行的时候当然就是running,调用完毕自然就是done,如果需要停止事件循环,就需要先把task取消,状态为cancel。

import asyncio


def foo(future, result):
    print(f"此时future的状态:{future}")
    print(f"设置future的结果:{result}")
    future.set_result(result)
    print(f"此时future的状态:{future}")


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    try:
        all_done = asyncio.Future()
        loop.call_soon(foo, all_done, "Future is done!")
        print("进入事件循环")
        result = loop.run_until_complete(all_done)
        print("返回结果", result)
    finally:
        print("关闭事件循环")
        loop.close()
    print("获取future的结果", all_done.result())

Future回调
Future 在完成的时候可以执行一些回调函数,回调函数按注册时的顺序进行调用:

组合协程
一系列的协程可以通过await链式的调用,但是有的时候我们需要在一个协程里等待多个协程,比如我们在一个协程里等待1000个异步网络请求,对于访问次序有没有要求的时候,就可以使用另外的关键字wait或gather来解决了。wait可以暂停一个协程,直到后台操作完成。

等待多个协程

tasks = [num(i) for i in range(10)]
    complete, pending = await asyncio.wait(tasks, timeout=0.5)

wait的使用:
在内部wait()使用一个set保存它创建的Task实例。因为set是无序的所以这也就是我们的任务不是顺序执行的原因。wait的返回值是一个元组,包括两个集合,分别表示已完成和未完成的任务。wait第二个参数为一个超时值
达到这个超时时间后,未完成的任务状态变为pending,
gather的使用

gather的作用和wait类似不同的是。
1.gather任务无法取消。
2.返回值是一个结果列表(complete,因为无法取消,只能是完成)
3.可以按照传入参数的顺序,顺序排列任务。(wait任务排列是随机的)
gather通常被用来阶段性的一个操作,做完第一步才能做第二步
1.step1和step2是并行运行的。
2.gather会等待最耗时的那个完成之后才返回结果,耗时总时间取决于其中任务最长时间的那个。

tasks = [num(i) for i in range(10)]
    complete = await asyncio.gather(*tasks)

参考文献
https://zhuanlan.zhihu.com/p/59671241

异步任务和定时任务的区别
在开发系统的过程中,通常会考虑到系统的性能问题,提升系统性能的一个重要思想就是“串行”改“并行”。说起“并行”自然离不开“异步”。
量化开发中的多策略并行执行用到的就是异步

定时任务,项目开发中经常需要执行一些固定时间执行的任务,比如需要在每天凌晨的时候,分析一次前一天的日志信息
量化开发中定时执行策略,用到的就是定时任务,单纯的定时任务是串行的(同步任务),如果定时任务在同一时间点需要同时执行多个,就涉及到异步

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

779醒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值