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

liftword6个月前 (12-27)技术文章56

概念:

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

相关文章

人生苦短,我要在VSCode里面用Python

轻沉 发自 浅度寺 量子位 出品 | 公众号 QbitAI在程序员圈子里,Visual Studio Code(以下简称VSCode)可以说是目前最火的代码编辑器之一了。它是微软出品的一款可扩展的轻量...

Python爬虫进阶教程(二):线程、协程

简介线程线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程中的最小单元,由线程ID、程序计数器、寄存器集合和堆栈共同组成。线程的引入减小了程序并发执行时的开销,提高了操作系统的并发性能...

Python编程之第1节(环境配置) python3.8配置环境变量

1,Python是一种动态解释型的编程语言,可以在Windows、UNIX、MAC等多种操作系统上使用,也可以在Java、.NET开发平台上使用。2,特点:Python使用C语言开发,但Python不...

Python的web框架对比及生产部署方案

Python Web框架对比Django特点:大而全,自带的功能特别特别多,有时候过于笨重Flask特点:小而精,自带的功能特别特别少,第三方模块特别特别多,比较依赖于第三方开发者flask自带一个服...