【Python】asyncio的使用(async、await关键字)

2023-05-16

1.协程

相比于线程和进程,协程显得更加轻量级,因为它是在函数直接进行切换,所以开销更小,也更灵活。之前有介绍过greenlet、gevent这样的协程库(【python】协程(greenlet、gevent)的简单使用),现在再来认识一下Python自带的协程库,asyncio

我们可以使用asyncio创建协程,把同步任务变成异步任务很简单,我们在定义函数的时候在前面加上async修饰,在耗时任务那行代码使用await修饰,这时候调用函数,它就会返回一个协程(coroutine)对象,然后调用asyncio.run()把协程对象丢进去就能执行了

import asyncio
import time


async def test(i, n):
    print(f"任务{i}将休眠{n}秒")
    await asyncio.sleep(n)
    print(f"任务{i}{n}秒后继续执行")


start_time = time.time()
asyncio.run(test(1, 1))
print(f'总用时{time.time() - start_time}秒')

执行输出如下

任务1将休眠1秒
任务1在1秒后继续执行
总用时1.0107183456420898秒

2.使用asyncio执行多任务

2.1 同步执行

假设有一个任务test(),需要执行三次,每次分别休眠2秒、1秒、3秒,如果是同步执行,一共需要耗时6秒

import time


def test(i, n):
    print(f"任务{i}将休眠{n}秒")
    time.sleep(n)
    print(f"任务{i}{n}秒后继续执行")


start_time = time.time()
task_list = [test(1, 2), test(2, 1), test(3, 3)]
print(f'总用时{time.time() - start_time}秒')

执行输出如下

任务1将休眠2秒
任务1在2秒后继续执行
任务2将休眠1秒
任务2在1秒后继续执行
任务3将休眠3秒
任务3在3秒后继续执行
总用时6.022886514663696秒
2.2 异步执行

我们使用asyncio把上面的多任务改成异步执行,就是使用async和await修饰一下。在调用执行的时候,需要创建一个事件循环,在事件循环里执行任务,这样它会在耗时的时候自动切换到其他任务。另外,最后要记得关闭事件循环。具体写法如下

import asyncio
import time


async def test(i, n):
    print(f"任务{i}将休眠{n}秒")
    await asyncio.sleep(n)
    print(f"任务{i}{n}秒后继续执行")


start_time = time.time()

task_list = [test(1, 2), test(2, 1), test(3, 3)]
loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(asyncio.wait(task_list))
finally:
    loop.close()
print(f'总用时{time.time() - start_time}秒')

执行输出如下

任务2将休眠1秒
任务3将休眠3秒
任务1将休眠2秒
任务2在1秒后继续执行
任务1在2秒后继续执行
任务3在3秒后继续执行
总用时2.9994614124298096秒

同步执行的时候需要6秒,改为异步执行需要3秒就行了

2.3 异步执行的返回值

如果想要得到异步任务的返回值,只需要使用asyncio.ensure_future()函数把任务包装成一个Task对象,执行完之后再调用它的result()方法就能得到返回值了

import asyncio
import time


async def test(i, n):
    print(f"任务{i}将休眠{n}秒")
    await asyncio.sleep(n)
    print(f"任务{i}{n}秒后继续执行")
    return f"任务{i}执行{n}秒后结束"


start_time = time.time()
task_list = [asyncio.ensure_future(test(1, 2)),
             asyncio.ensure_future(test(2, 1)),
             asyncio.ensure_future(test(3, 3))]
loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(asyncio.wait(task_list))
finally:
    loop.close()
task_result = [task.result() for task in task_list]
print(f"task_result: {task_result}")
print(f'总用时{time.time() - start_time}秒')

执行输出如下

任务1将休眠2秒
任务2将休眠1秒
任务3将休眠3秒
任务2在1秒后继续执行
任务1在2秒后继续执行
任务3在3秒后继续执行
task_result: ['任务1执行2秒后结束', '任务2执行1秒后结束', '任务3执行3秒后结束']
总用时3.0089805126190186秒

3.举例:异步下载图片

如果是一张一张按顺序下载图片,我这里大概需要一分多钟下载完39张图片,改为asyncio异步下载大概需要三四秒。

注意,发起异步网络请求不能使用requests,因为它是同步的,而应该aiohttp或httpx,我这里使用的是aiohttp

import asyncio
import os.path
import time

import aiohttp
import requests


def get_img_url():
    img_url_list = list()
    res = requests.get("https://game.gtimg.cn/images/lol/act/img/js/hero/1.js").json()

    for skin in res.get("skins"):
        if skin["mainImg"]:
            img_url_list.append(skin["mainImg"])
        elif skin["iconImg"]:
            img_url_list.append(skin["iconImg"])
        elif skin["sourceImg"]:
            img_url_list.append(skin["sourceImg"])
        elif skin["chromaImg"]:
            img_url_list.append(skin["chromaImg"])
    return img_url_list


async def download_img(img_url):
    save_path = "./图片"
    if not os.path.exists(save_path):
        os.mkdir(save_path)
    name = os.path.split(img_url)[1]
    async with aiohttp.ClientSession() as session:
        async with session.get(img_url) as res:
            with open(os.path.join(save_path, name), "wb") as f:
                f.write(await res.content.read())


if __name__ == '__main__':
    url_list = get_img_url()
    print(len(url_list), url_list)
    start_time = time.time()
    task_list = [download_img(url) for url in url_list]
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(task_list))
    print("spent_time:", time.time() - start_time)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Python】asyncio的使用(async、await关键字) 的相关文章

  • 系统的TPS不达标,此时如何优化

    对系统进行压测 xff0c 比如每秒压个几百请求到几千请求 xff0c 甚至上万请求 xff0c 此时发现死活压不上去 xff0c 压来压去 xff0c 你的系统最多每秒就处理几百个请求 xff0c 根本到不了几千个请求 xff0c 此时就
  • f103 hal库中PWM的使用方法

    1 初始化复用管脚 span class token keyword void span span class token function BEEP Init span span class token punctuation span
  • EFR32FG14 使用定时器TIMER做毫秒级定时

    EFR32FG14 使用定时器TIMER做毫秒级定时 1 添加头文件 span class token macro property span class token directive keyword include span span
  • EFR32FG14 UART的使用方法

    EFR32FG14 UART的使用方法 1 初始化串口 xff08 PA0 gt TX PA1 gt RX xff09 span class token keyword void span span class token function
  • EFR32解锁方法

    EFR32锁住的解锁方法 在某些情况下 xff0c 芯片可能会被锁住 xff0c 导致J LINK等工具连接不上 xff0c 可以参考如下方法 xff1a 1 进入commander exe所在的目录 xff1b 按住Shift 43 右键
  • TFT1.44寸屏ST7735S屏幕使用-stm32f103c8t6

    TFT1 44寸屏ST7735S屏幕使用 stm32f103c8t6 K xff1a 背景灯 RESET xff1a tft复位 RS xff1a 控制线 xff08 数据或者命令 xff09 SDA xff1a 数据线 xff08 SPI
  • 8421拨码器 R7H3-16 的使用

    8421拨码器 R7H3 16 的使用 使用方法极其简单 xff0c 1 2 4 8脚分解与单片机的4个IO脚连接 xff0c IO配置为输入即可 0代表低电平 xff0c 1代表高电平 档位 脚位124801111101112101130
  • Ubuntu 18 上不了网解决方法

    Ubuntu 18 出现网络异常 1 打开命令行 Ctrl 43 Alt 43 t 2 关闭网络 sudo service network manager stop 3 删除网络 sudo rm var lib NetworkManager
  • GPIO引脚 模拟 IIC(软件IIC)

    GPIO引脚 模拟 IIC 软件IIC IIC总线在传输数据的过程中一共有三种类型信号 xff0c 分别为 xff1a 开始信号 结束信号和应答信号 IIC总线的时序图 xff1a 空闲状态 当IIC总线的数据线SDA和时钟线SCL两条信号
  • ESP32/ESP8266 MQTT协议接入阿里云(一)

    ESP32 ESP8266 MQTT协议接入阿里云 xff08 一 xff09 1 搭建阿里云环境 xff08 1 xff09 跳转连接 xff1a https iot console aliyun com lk summary new x
  • ESP32/ESP8266 MQTT协议接入阿里云(二)

    ESP32 ESP8266 MQTT协议接入阿里云 xff08 二 xff09 1 在连接阿里云之前 xff0c 需要先了解MQTT的连接协议 CONNECT 协议格式 xff1a 固定包头 43 可变包头 43 有效载体 xff08 1
  • https是如何验证证书的有效性的

    证书验证的过程是使用非对称加密的 xff0c 客户端对服务器端发起请求 xff0c 服务器返回一个证书 xff0c 客户端验证这个证书的合法性 xff0c 如果这个证书是合法的 xff0c 那么就生成一个随机值 xff0c 利用这个随机值作
  • Kali Linux 更新源

    vi etc apt source list 添加下列更新源 中科大 deb http mirrors ustc edu cn kali kali rolling main non free contrib deb src http mir
  • 安装所有Kali 工具包

    apt get kali linux all
  • 路由选路三原则

    路由选路的三原则 最长掩码匹配原则AD值 Administrative Distance 通告距离 路由类型AD值Connect0Static1EIGRP Summary5EBGP20EIGRP 内部90OSPF110RIP120EIGRP
  • OSPF7种状态

  • CentOS 7 由原来的root@localhost~# 变成了-bash-4.2#

    发生这种原因可能是 root 目录下缺少了几个配置 bashrc 和 bash profile 进入 etc skel 目录下 将 bashrc 和 bash profile复制到 root 目录下 1 cp etc skel bashrc
  • Kali 中 dnsdict6 安装过程

    更新下载源 文件目录 etc apt source list 增加源deb http mirrors ustc edu cn kali kali rolling main non free contrib deb src http mirr
  • 在CentOS 7上搭建代理服务器(Socks 5)

    安装环境配置 1 yum install gcc 2 yum install openldap devel 3 yum install pam devel 4 yum install openssl devel 安装Socks 5 wget
  • Archlinux 安装教程 - 附详细图文(一)

    博主声明 xff1a 转载请在开头附加本文链接及作者信息 xff0c 并标记为转载 本文由博主 威威喵 原创 xff0c 请多支持与指教 本文首发于此 博主 xff1a 威威喵 博客主页 xff1a https blog csdn net

随机推荐