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

liftword5个月前 (12-27)技术文章47

概念:

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启航:30天编程速成之旅(第26天)- pathlib

喜欢的条友记得关注、点赞、转发、收藏,你们的支持就是我最大的动力源泉。前期基础教程:「Python3.11.0」手把手教你安装最新版Python运行环境讲讲Python环境使用Pip命令快速下载各类库...

Micropython 玩转硬件系列1:环境搭建

1.引言最近几年Python语言非常火,听说小学生都开始学Python了,让我这个中年人感到一丝丝压力。为了以后最起码能辅导辅导孩子,咱也得学学啊。学Python干什么用呢?我这本身是做嵌入式的,听说...

Python协程之asyncio Python协程之间共享变量

asyncio 是 Python 中的异步IO库,用来编写并发协程,适用于IO阻塞且需要大量并发的场景,例如爬虫、文件读写。私信小编01即可获取Python学习资料asyncio 在 Python3....

一文带您了解Python中的并发:异步(Asyncio)和多线程(Thread)

Python以其简单性和多样性而闻名,是一种适用于广泛应用领域的编程语言。在处理多个任务并发时,Python提供了两种主要方法:Asyncio 用于异步编程,Multithreading 用于管理多个...

Python 什么情况下会生成 pyc 文件?

作者:折木奉太郎来源:https://www.zhihu.com/question/30296617/answer/112564303作为 Python 爱好者,需要了解 .py 脚本的基本运行机制及...