python asyncio -- 异步编程 python异步编程用在哪里
概念:
asyncio 是 python3.4 版本引入的标准库,直接内置了对异步IO的支持。本质上asyncio的编程模型是一个消息循环,asyncio 模块内部实现了EventLoop,把需要执行的协程扔到EventLoop 中执行,就实现了异步IO。
首先注意几个概念:
coroutine: 协程,通过async定义的方法就是一个coroutine,当我们定义一个coroutine的时候,并不会执行它,只有当这个coroutine被转换为task,并且注册到event loop里面时候,才有可能被event loop调用执行。
event loop:事件循环,会不断地检测每个task的状态,当某个task可以被执行的时候,就会执行这个任务,直到这个任务结束或者这个任务遇到一个新的await 标记的新的coroutine。
task:coroutine 需要被转换成task,才可以被执行。
await:作用是将目前代码执行权交还给event loop,并且向event loop 注册一个新的coroutine。
案例:
import asyncio
import time
async def say_after(delay,what):
await asyncio.sleep(delay)
return f"{what} -- {delay}"
async def main():
ret = await asyncio.gather(say_after(1,"hello"),say_after(2,"world"))
print("finished)
asyncio.run(main())
分析例子的执行过程:
--1 main 函数被定义为一个coroutine,并通过asyncio.run 执行,这里包含了coroutine转成task的过程
--2 asyncio.gather 将两个coroutine转成task,并且注册到event loop 内
--3 await 告诉event loop 我有两个task需要执行,执行完之后才能接着执行当前的coroutine
--4 此时event loop 内部有main 和 hello world 三个task,并且main 需要等待hello 和 world 执行完
--5 执行hello 遇到sleep coroutine,交还控制权,并且注册了sleep 这个task,此时有4个task
--6 hello sleep task 任务状态等待跳过,执行world task 这个任务,同理遇到sleep coroutine,注册交还控制权
--7 此时有5个task: main -- hello --hello_sleep -- world -- world_sleep,并且main需要等hello world,hello和world需要分别等各自的sleep。event loop 就会在两个sleep task 之间来回检测,直到有任务结束,然后逐层退出,从表面看起来两个sleep是同时执行的。
我们也可以显示地创建task,然后再让他们await住:
import time
import asyncio
async def task1():
print("Starting task 1")
await asyncio.sleep(1)
print("task 1 finished")
async def task2():
print("Starting task 2")
await asyncio.sleep(2)
print("task 2 finished")
async def task3():
print("Starting task 3")
await asyncio.sleep(3)
print("task 3 finished")
if __name__ == "__main__":
async def main():
startTime = time.time()
tasks = [asyncio.create_task(task1()), asyncio.create_task(task2()), asyncio.create_task(task3())]
for task in tasks:
await task
print("Total time: ",time.time() - startTime)
asyncio.run(main())
再附上一个网络请求的例子:
import asyncio
async def wget(host):
print(f"wget {host}...")
# 连接80端口:
reader, writer = await asyncio.open_connection(host, 80)
# 发送HTTP请求:
header = f"GET / HTTP/1.0\r\nHost: {host}\r\n\r\n"
writer.write(header.encode("utf-8"))
await writer.drain()
# 读取HTTP响应:
while True:
line = await reader.readline()
if line == b"\r\n":
break
print("%s header > %s" % (host, line.decode("utf-8").rstrip()))
# Ignore the body, close the socket
writer.close()
await writer.wait_closed()
print(f"Done {host}.")
async def main():
await asyncio.gather(wget("www.sina.com.cn"), wget("www.sohu.com"), wget("www.163.com"))
asyncio.run(main())
await writer.drain() 和 await writer.wait_closed() 都是异步的,因此几个task之间可以并发地去等待,起到加速的作用。
总结:
--1 asyncio 只有一个线程,因此不需要锁的机制,只有在函数内部有异步调用的时候才有效果
--2 asyncio 内部是一个event loop 循环检测任务状态,执行可以被执行的任务
--3 某个任务在执行中切换到其他任务的方式有两种,一种是任务结束了,一种显示地调用await交还控制权