python asyncio -- 异步编程 python异步编程用在哪里

liftword4个月前 (12-27)技术文章38

概念:

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交还控制权

相关文章

赶紧收藏!编程python基础知识,本文给你全部整理好了

想一起学习编程Python的同学,趁我粉丝少,可以留言、私信领编程资料~Python基础入门既然学习 Python,那么至少得了解下这门编程语言,知道 Python 代码执行过程吧。Python 的历...

人生苦短,不光要用 Python,还要在 VSCode 里用 | 原力计划

作者 | imbennyguo责编 | 胡巍巍出品 | CSDN博客在程序员圈子里,Visual Studio Code(以下简称VSCode)可以说是目前最火的代码编辑器之一了。它是微软出品的一款可...

Windows安装Python教程 windows下python安装教程

1-1 安装Python 3.7.0 解释器首先需要说一下,Windows系统主要讲解Win 7环境下Python3.7.0的安装操作。推荐Win XP和win 10的Python解释器版本为Pyth...