Python Asyncio以及Futures并发编程实践

海到尽头天作岸,山登绝顶我为峰。

1 前言

之前已经分享了许多篇 Python 数据分析的文章,在本文中将继续分享关于 Python 并发编程的内容。 asyncio 是并发代码编码中重要的类库,使用 async/await 关键字以及其它方法实现并发编程,在 IO 密集型和高并发网络、网络服务、数据爬虫编程中应用十分广泛。

2 核心概念

  • Event Loop 事件循环:管理所有的时间,负责携程的跳读和执行。
  • Coroutine 协程:使用async定义的函数,是异步操作的基础。协程可以在等待I/O操作时挂起,让出控制权给事件循环。可以通过 await 暂停执行并等待结果。
  • Task 任务:对协程进行封装,对协程进行调度指挥。通常任务是非阻塞的,可以并发运行任务。
  • Future 异步任务的最终结果,通常不需要直接创建。
  • Awaitable 可等待对象。包括协程、任务和Future等,可以在await表达式中使用。

Asyncio 和其它 Python 程序一样是单线程的,它只有一个主线程,但是可以分为多个不同的任务进行执行。这里的任务就是 Future 对象,这些不同的任务 event loop 所控制。event loop 会维护两个任务列表,预备状态(空闲状态的任务,随时准备运行)和等待状态(运行状态的任务,正在等待外部操作的完成,比如IO操作)。

3 基础用法

常用的异步程序代码如下图所示:

python 复制代码
async def async_base_method():
    # 模拟 io 操作
    print("异步方法开始")
    # 设置超时时间
    await asyncio.sleep(3)
    print("异步方法结束")
    
# 传入异步方法,是否采用 debug 模式 
asyncio.run(async_base_method(), debug= True)

使用创建异步任务的方式执行和超时的配置如下:

python 复制代码
# 创建任务进行操作
async def async_base_task():
    task1 = asyncio.create_task(async_base_method("d1"))
    task2 = asyncio.create_task(async_base_method("d2"))
    # 等待任务完成,并且设置抛出异常信息
    await asyncio.gather(task1, task2, return_exceptions=True)
    # 取消任务处理
    task1.cancel()
    # 使用shield防止任务被取消
    await asyncio.shield(task)

# 配置超时
async def timeout_sample():
    try:
       # 设置超时时间为 10 秒钟 
       await  asyncio.wait_for(async_base_method("d3"), timeout=10)
       # 使用 wait api   
       tasks = [task1, task2, task3]
       # 等待所有任务完成,返回 (完成的任务集, 未完成的任务集)
       done, pending = await asyncio.wait(tasks)
    except asyncio.TimeoutError as e:
        print("time out ", e)

4 异步IO操作

异步的IO操作常用的是文件读写以及网络读写,具体的实例如下所示:

python 复制代码
import aiofiles
import aiohttp
# 异步任务进行文件读写操作,需要安装类库 aiofiles
async def async_file_io():
    # 异步读取文件
    async with aiofiles.open('data.txt', 'r') as f:
        contents = await f.read()
    # 异步写入文件
    async with aiofiles.open('output.txt', 'w') as f:
        await f.write(contents)

# 异步http请求获取返回结果,这里使用的是 aiohttp,而不是 requests,这是因为 Asyncio 不兼容 requests。
async def http_result(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.text()
# 获取数据结果
async def async_query():
    urls = ['https://api.example.com', 'https://api.example.cn']
    tasks = [http_result(url) for url in urls]
    results = await asyncio.gather(*tasks)
    for result in results:
        print(result)  # 打印最终的结果

5 event_loop 操作

此外,还有 event_loop 相关的操作,如下所示:

python 复制代码
# 获取一个 event_loop
loop = asyncio.get_event_loop()
try:
    # 获取当前的事件循环,执行方法直到方法执行完成
    loop.run_until_complete(handle_method())
finally:
    loop.close()

6 并发编程-future

Futures 模块位于 concurrent.futuresasyncio 中,这个模块带有延迟操作。Futures 会将处于等待状态的任务操作封装起来放到队列中。这些任务的状态可以查询状态或者查询异常状态,也可以等待执行完成后获取结果。

python 复制代码
# 下载数据
def download_one_image(url):
    resp = requests.get(url)
    print('read {} from {}'.format(len(resp.content), url))

def download_all_images(address):
    # 创建线程池执行任务
    with concurrent.futures.ThreadPoolExecutor(thread_name_prefix="app_executor", max_workers=5) as executor:
        # 这里的 executor.map 和 python 内置函数类似,需要循环 address 并发调用 download_one_image
        executor.map(download_one_image, address)

def download_all_images_future(address):
    # 使用线程池进行下载数据,返回 future 对象
    with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
        to_do = []
        for node in address:
            future = executor.submit(download_one_image, node)
            to_do.append(future)
        for future in concurrent.futures.as_completed(to_do):
            future.result()

if __name__ == '__main__':

    urls = ["https://pic.netbian.com/uploads/allimg/250219/233238-173997915843ef.jpg",
            "https://pic.netbian.com/uploads/allimg/250304/163813-1741077493fa4e.jpg"]

    download_all_images(urls)

7 总结

在本文中主要介绍了 PythonAsyncioFutures 的并发编程编程实践,总结了其主页的使用方法。相关代码已经上传至 github, 欢迎大家 star, 项目地址 fund_python

相关推荐
MoRanzhi12032 分钟前
11. Pandas 数据分类与区间分组(cut 与 qcut)
人工智能·python·机器学习·数学建模·分类·数据挖掘·pandas
Pr Young4 分钟前
MVCC 多版本并发控制
数据库·后端·mysql
IT_陈寒11 分钟前
Java并发编程避坑指南:7个常见陷阱与性能提升30%的解决方案
前端·人工智能·后端
码界筑梦坊21 分钟前
276-基于Python的爱奇艺视频数据可视化分析系统
开发语言·python·信息可视化
牧码岛28 分钟前
服务端之NestJS接口响应message编写规范详解、写给前后端都舒服的接口、API提示信息标准化
服务器·后端·node.js·nestjs
我的xiaodoujiao1 小时前
从 0 到 1 搭建 Python 语言 Web UI自动化测试学习系列 9--基础知识 5--常用函数 3
前端·python·测试工具·ui
星秀日2 小时前
框架--SpringBoot
java·spring boot·后端
B站计算机毕业设计之家5 小时前
智慧交通项目:Python+PySide6 车辆检测系统 YOLOv8+OpenCV 自定义视频 自定义检测区域 (源码+文档)✅
大数据·python·opencv·yolo·智慧交通·交通·车流量
java1234_小锋6 小时前
TensorFlow2 Python深度学习 - 深度学习概述
python·深度学习·tensorflow·tensorflow2·python深度学习
迈火7 小时前
PuLID_ComfyUI:ComfyUI中的图像生成强化插件
开发语言·人工智能·python·深度学习·计算机视觉·stable diffusion·语音识别