python asyncio的作用

news2025/6/8 8:59:13

协程是可以暂停运行和恢复运行的函数。协程函数是用async定义的函数。它与普通的函数最大的区别是,当执行的时候不会真的执行里面的代码,而是返回一个协程对象,在执行协程对象时才执行里面真正的代码。

例如代码:

async def coroutine_function():
    print("this is a coroutine function")

print(coroutine_function())

执行结果:

<coroutine object coroutine_function at 0x10a7fc7c0>
/Users/4bu/code/neimeng-python/test/test_async.py:5: RuntimeWarning: coroutine 'coroutine_function' was never awaited
  print(coroutine_function())
RuntimeWarning: Enable tracemalloc to get the object allocation traceback

从打印结果可以看出返回的是一个协程对象coroutine object,当运行协程对象才会执行里面的代码,并且可以暂停运行和恢复运行。

当出现await时,就会暂停运行,让出控制权,等await后的函数执行完成后,再请求控制权回来恢复运行(拥有控制权的协程可以运行,没有控制权的只有等待)。

当协程暂停运行的时候,CPU开始事件循环,用来调度协程执行,握着控制权,循环往复做三件事情:

  1. 检查协程。拿到控制权后,就开始检查有没有可以执行的协程。
  2. 让出控制。将控制权传递给可以执行的协程。
  3. 等待协程。等当前协程暂停或者执行完成,放开控制权给自己。然后再回到第一步。

事件循环如何知道哪些协程可以执行,哪些协程不可以执行,这就需要任务。任务是对协程的封装,除了包含协程本身,还包含协程的状态,比如准备执行,正在执行,已完成等等。让事件循环知道协程是否可以运行。只要一个协程被封装为任务,那么就会被事件循环调度执行。

不添加协程的代码

例如不添加协程的代码:

from time import sleep, perf_counter

def fetch_url(url):
    print(f"Fetching {url}")
    sleep(1)
    print(f"Finished {url}")
    return 'url_content'

def read_file(file_path):
    print(f"Reading {file_path}")
    sleep(1)
    print(f"Finished {file_path}")

def main():
    url = 'example.com'
    file_path = 'example.txt'
    fetch_result = fetch_url(url)
    read_result = read_file(file_path)

if __name__ == '__main__':
    start = perf_counter()
    main()
    end = perf_counter()
    print(f"Time taken: {end - start}")

输出结果:

Fetching example.com
Finished example.com
Reading example.txt
Finished example.txt
Time taken: 2.0090410669999983

没有使用协程的方式,是用时2秒。

编写协程

  1. 定义协程函数,在需要暂停的地方使用await
  2. 将协程包装为任务
  3. 建立事件循环
    将其改写为使用协程的方式进行:
from time import sleep, perf_counter
import asyncio

async def fetch_url(url):
    print(f"Fetching {url}")
    # 如何保证在当前协程暂停的情况下,await后的函数能够执行
    # 那就是await后的函数也必须是协程函数,因此需要使用asyncio.sleep()替换sleep()
    # await同时会将后面的协程包装乘任务,让事件循环调度
    await asyncio.sleep(1)
    print(f"Finished {url}")
    return 'url_content'

async def read_file(file_path):
    print(f"Reading {file_path}")
    await asyncio.sleep(1)
    print(f"Finished {file_path}")

async def main():
    url = 'example.com'
    file_path = 'example.txt'
    task1 = asyncio.create_task(fetch_url(url))
    task2 = asyncio.create_task(read_file(file_path))
    fetch_result = await task1
    read_result = await task2
    print(fetch_result)
    print(read_result)

if __name__ == '__main__':
    start = perf_counter()
    # asyncio.run(main())
    main()
    end = perf_counter()
    print(f"Time taken: {end - start}")

await表明了当前协程要暂停运行,等完成了,后面的语句才会继续运行。如果await后面是一个协程,则需要包装成一个任务,但如果已经是一个协程任务了,那就不需要再包装了。等到await后的协程执行完了,返回await的协程执行结果。

await的作用:

  1. 暂停当前协程
  2. 包装await后的协程为任务
  3. 获取await后的协程结果

输出结果如下:

Fetching example.com
Reading example.txt
Finished example.com
Finished example.txt
url_content
file_content
Time taken: 1.002050409999356

实现协程异步的方式:

  1. 定义协程函数
  2. 包装协程为任务
  3. 建立事件循环

将协程包装为任务有两种方式:

  1. 手动。先用一个语句创建task,然后再用另一个语句获取执行结果。这种方式可以检查task的执行状态,或者执行取消task。上面的方式就是手动方式。
  2. 自动。一个语句接收协程后,直接返回执行结果。这种方式更方便简洁。

自动包装协程任务

  • asyncio.gather(),会等所有协程执行完成后才返回结果,代码如下:
from os import read
from time import sleep, perf_counter
import asyncio

async def fetch_url(url):
    print(f"Fetching {url}")
    # 如何保证在当前协程暂停的情况下,await后的函数能够执行
    # 那就是await后的函数也必须是协程函数,因此需要使用asyncio.sleep()替换sleep()
    # await同时会将后面的协程包装成任务,让事件循环调度
    await asyncio.sleep(1)
    print(f"Finished {url}")
    return 'url_content'

async def read_file(file_path):
    print(f"Reading {file_path}")
    await asyncio.sleep(1)
    print(f"Finished {file_path}")
    return 'file_content'

async def main():
    url = 'example.com'
    file_path = 'example.txt'
    result = await asyncio.gather(fetch_url(url), read_file(file_path))
    print(result)

if __name__ == '__main__':
    start = perf_counter()
    # asyncio.run()会创建一个事件循环,然后将main()函数包装成任务,让事件循环调度
    asyncio.run(main())
    end = perf_counter()
    print(f"Time taken: {end - start}")

输出结果如下:

Fetching example.com
Reading example.txt
Finished example.com
Finished example.txt
['url_content', 'file_content']
Time taken: 1.0032507400010218
  • asyncio.as_completed,不会等所有协程都完成后才返回,而是有一个运行完就返回一个结果
from os import read
from time import sleep, perf_counter
import asyncio

async def fetch_url(url):
    print(f"Fetching {url}")
    # 如何保证在当前协程暂停的情况下,await后的函数能够执行
    # 那就是await后的函数也必须是协程函数,因此需要使用asyncio.sleep()替换sleep()
    # await同时会将后面的协程包装成任务,让事件循环调度
    await asyncio.sleep(1)
    print(f"Finished {url}")
    return 'url_content'

async def read_file(file_path):
    print(f"Reading {file_path}")
    await asyncio.sleep(1)
    print(f"Finished {file_path}")
    return 'file_content'

async def main():
    url = 'example.com'
    file_path = 'example.txt'
    # 返回迭代器,按照协程完成的顺序依次输出
    results = asyncio.as_completed([fetch_url(url), read_file(file_path)])
    for result in results:
        # 使用await获取执行结果
        print(await result)

if __name__ == '__main__':
    start = perf_counter()
    # asyncio.run()会创建一个事件循环,然后将main()函数包装成任务,让事件循环调度
    asyncio.run(main())
    end = perf_counter()
    print(f"Time taken: {end - start}")

其他的异步库

除了asyncio之外,处理请求可以使用aiohttp,处理文件可以使用aiofiles

pip install aiohttp
pip install aiofiles
from os import read
from time import sleep, perf_counter
import asyncio
import aiohttp
import aiofiles
import ssl

async def fetch_url(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url, timeout=aiohttp.ClientTimeout(total=15)) as response:
            return await response.text()

async def read_file(file_path):
    async with aiofiles.open(file_path, 'r') as f:
        return await f.read() # read the entire file as a string and return it as a coroutine.

async def main():
    # url = 'http://jsonplaceholder.typicode.com/posts'
    url = 'http://www.baidu.com'
    file_path = 'example.txt'
    results = asyncio.as_completed([fetch_url(url), read_file(file_path)])
    for result in results:
        print(await result)

if __name__ == '__main__':
    start = perf_counter()
    # asyncio.run()会创建一个事件循环,然后将main()函数包装成任务,让事件循环调度
    asyncio.run(main())
    end = perf_counter()
    print(f"Time taken: {end - start}")

在新线程中运行同步函数

如何在新线程中运行同步函数,不阻塞事件循环。

from os import read
from time import sleep, perf_counter
import asyncio
import aiohttp
import aiofiles
import ssl

async def fetch_url(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url, timeout=aiohttp.ClientTimeout(total=15)) as response:
            return await response.text()

async def read_file(file_path):
    async with aiofiles.open(file_path, 'r') as f:
        return await f.read() # read the entire file as a string and return it as a coroutine.

# 这个函数可以在异步上下文中运行,但是它是一个阻塞函数,所以需要使用asyncio.to_thread()将其包装成一个协程。
# 这样,这个函数就可以在异步上下文中运行了。
def foo(*args):
    sleep(1)
    return 'foo'

async def main():
    # url = 'http://jsonplaceholder.typicode.com/posts'
    url = 'http://www.baidu.com'
    file_path = 'example.txt'
    results = asyncio.as_completed([fetch_url(url), read_file(file_path), asyncio.to_thread(foo, 'bar')])
    for result in results:
        print(await result)

if __name__ == '__main__':
    start = perf_counter()
    # asyncio.run()会创建一个事件循环,然后将main()函数包装成任务,让事件循环调度
    asyncio.run(main())
    end = perf_counter()
    print(f"Time taken: {end - start}")

参考B站学习视频

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2403950.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【大模型:知识图谱】--3.py2neo连接图数据库neo4j

【图数据库】--Neo4j 安装_neo4j安装-CSDN博客 需要打开图数据库Neo4j&#xff0c; neo4j console 目录 1.图数据库--连接 2.图数据库--操作 2.1.创建节点 2.2.删除节点 2.3.增改属性 2.4.建立关系 2.5.查询节点 2.6.查询关系 3.图数据库--实例 1.图数据库--连接 fr…

如何理解机器人课程的技术壁垒~壁垒和赚钱是两件不同的事情

答疑&#xff1a; 有部分朋友私聊说博客内容&#xff0c;越来越不适合人类阅读习惯…… 可以做这种理解&#xff0c;我从23年之后&#xff0c;博客会不会就是写给机器看的。 或者说我在以黑盒方式测试AI推荐的风格。 主观-客观-主观螺旋式发展过程。 2015最早的一篇博客重…

selinux firewalld

一、selinux 1.说明 SELinux 是 Security-Enhanced Linux 的缩写&#xff0c;意思是安全强化的 linux&#xff1b; SELinux 主要由美国国家安全局&#xff08;NSA&#xff09;开发&#xff0c;当初开发的目的是为了避免资源的误用 DAC&#xff08;Discretionary Access Cont…

408第一季 - 数据结构 - 字符串和KMP算法

闲聊 这章属于难点但考频低 3个名词记一下&#xff1a;模式匹配&#xff0c;主串&#xff0c;字串&#xff08;模式串&#xff09; 举个例子 主串 aabaaaabaab 字串 aabaab 模式匹配 从主串找到字串 暴力解法 也是不多说 很暴力就是了 KMP算法 next数组 它只和字串有关 先…

如何查看自己电脑安装的Java——JDK

开始->运行->然后输入cmd进入dos界面 &#xff08;快捷键windows->输入cmd&#xff09; 输入java -version&#xff0c;回车 出现了一下信息就是安装了jdk 输入java -verbose&#xff0c;回车 查看安装目录

电力系统时间同步系统之三

2.6 电力系统时间同步装置 时间同步装置主要完成时间信号和时间信息的同步传递&#xff0c;并提供相应的时间格式和物理接口。时间同步装置主要由三大部分组成&#xff1a;时间输入、内部时钟和时间输出&#xff0c;如图 2-25 所示。输入装置的时间信号和时间信息的精度必须不…

火语言RPA--界面应用详解

新建一个界面应用后&#xff0c;软件将自动弹出一个界面设计器&#xff0c;本篇将介绍下流程设计器中各部分的功能。 UI控件列表 显示软件中自带的所有UI控件流程库 流程是颗粒组件的容器&#xff0c;可在建立的流程中添加颗粒组件编写成规则流程。 流程编辑好后再绑定UI控件…

基于Spring Boot的云音乐平台设计与实现

基于Spring Boot的云音乐平台设计与实现——集成协同过滤推荐算法的全栈项目实战 &#x1f4d6; 文章目录 项目概述技术选型与架构设计数据库设计后端核心功能实现推荐算法设计与实现前端交互设计系统优化与性能提升项目部署与测试总结与展望 项目概述 &#x1f3af; 项目背…

Neovim - 打造一款属于自己的编辑器(一)

文章目录 前言&#xff08;劝退&#xff09;neovim 安装neovim 配置配置文件位置第一个 hello world 代码拆分 neovim 配置正式配置 neovim基础配置自定义键位Lazy 插件管理器配置tokyonight 插件配置BufferLine 插件配置自动补全括号 / 引号 插件配置 前言&#xff08;劝退&am…

RAG检索系统的两大核心利器——Embedding模型和Rerank模型

在RAG系统中&#xff0c;有两个非常重要的模型一个是Embedding模型&#xff0c;另一个则是Rerank模型&#xff1b;这两个模型在RAG中扮演着重要角色。 Embedding模型的作用是把数据向量化&#xff0c;通过降维的方式&#xff0c;使得可以通过欧式距离&#xff0c;余弦函数等计算…

CLion社区免费后,使用CLion开发STM32相关工具资源汇总与入门教程

Clion下载与配置 Clion推出社区免费&#xff0c;就是需要注册一个账号使用&#xff0c;大家就不用去找破解版版本了&#xff0c;jetbrains家的IDEA用过的都说好&#xff0c;这里嵌入式领域也推荐使用。 CLion官网下载地址 安装没有什么特别&#xff0c;下一步就好。 启动登录…

第21讲、Odoo 18 配置机制详解

Odoo 18 配置机制详解&#xff1a;res.config.settings 与 ir.config_parameter 原理与实战指南 在现代企业信息化系统中&#xff0c;灵活且可维护的系统参数配置是模块开发的核心能力之一。Odoo 作为一款高度模块化的企业管理软件&#xff0c;其参数配置机制主要依赖于两个关…

【计算机网络】Linux下简单的TCP服务器(超详细)

服务端 创建套接字 &#x1f4bb;我们将TCP服务器封装成一个类&#xff0c;当我们定义出一个服务器对象后需要马上对服务器进行初始化&#xff0c;而初始化TCP服务器要做的第一件事就是创建套接字。 TCP服务器在调用socket函数创建套接字时&#xff0c;参数设置如下&#xff1…

最新Spring Security实战教程(十七)企业级安全方案设计 - 多因素认证(MFA)实现

&#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Micro麦可乐的博客 &#x1f425;《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程&#xff0c;入门到实战 &#x1f33a;《RabbitMQ》…

html+css+js趣味小游戏~Cookie Clicker放置休闲(附源码)

下面是一个简单的记忆卡片配对游戏的完整代码&#xff0c;使用HTML、CSS和JavaScript实现&#xff1a; html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"wid…

SDC命令详解:使用set_propagated_clock命令进行约束

相关阅读 SDC命令详解https://blog.csdn.net/weixin_45791458/category_12931432.html?spm1001.2014.3001.5482 目录 指定端口列表/集合 简单使用 注意事项 传播时钟是在进行了时钟树综合后&#xff0c;使用set_propagated_clock命令可以将一个理想时钟转换为传播时钟&#x…

win32相关(消息Hook)

消息Hook 要想实现消息Hook需要使用到三个相关的Api SetWindowsHookEx // 设置钩子CallNextHookEx // 将钩子信息传递到当前钩子链中的下一个子程序UnhookWindowsHookEx // 卸载钩子 我们编写的消息钩子需要将设置钩子的函数写到dll里面&#xff0c;当钩住一个线程后&#xff…

mysql 页的理解和实际分析

目录 页&#xff08;Page&#xff09;是 Innodb 存储引擎用于管理数据的最小磁盘单位B树的一般高度记录在页中的存储 innodb ibd文件innodb 页类型分析ibd文件查看数据表的行格式查看ibd文件 分析 ibd的第4个页&#xff1a;B-tree Node类型先分析File Header(38字节-描述页信息…

构建 MCP 服务器:第 2 部分 — 使用资源模板扩展资源

该图像是使用 AI 图像创建程序创建的。 这个故事是在多位人工智能助手的帮助下写成的。 这是构建MCP 服务器教程&#xff08;共四部分&#xff09;的第二部分。在第一部分中&#xff0c;我们使用基本资源创建了第一个 MCP 服务器。现在&#xff0c;我们将使用资源模板扩展服务…

【算法设计与分析】实验——汽车加油问题, 删数问题(算法实现:代码,测试用例,结果分析,算法思路分析,总结)

说明&#xff1a;博主是大学生&#xff0c;有一门课是算法设计与分析&#xff0c;这是博主记录课程实验报告的内容&#xff0c;题目是老师给的&#xff0c;其他内容和代码均为原创&#xff0c;可以参考学习&#xff0c;转载和搬运需评论吱声并注明出处哦。 4-1算法实现题 汽车…