Python中asyncio的多种用法举例(异步同步)

 更新时间:2024年11月13日 09:47:49   作者:码农葫芦侠  
这篇文章主要给大家介绍了关于Python中asyncio的多种用法,包括顺序执行非异步任务、顺序执行异步任务、并行执行异步任务以及并行执行非异步任务,通过使用asyncio模块,可以有效地提高程序的执行效率,尤其是在处理I/O密集型任务时,需要的朋友可以参考下

1 引言 

Python 的 asyncio 模块为异步编程提供了强大的支持,但在某些场景下,我们可能需要处理异步任务与非异步(同步)任务的顺序执行或并行执行。本篇文章将逐步带你了解如何在 Python 中处理这些不同类型的任务。

2 顺序执行非异步任务 

在日常编程中,最常见的情况之一就是顺序执行一系列非异步(同步)的任务。这些任务在同一个线程中执行,通常会阻塞主程序的运行,直到任务完成。

示例代码:

import time

def blocking_task(id):
    print(f"Blocking task {id} started")
    time.sleep(2)  # 模拟一个阻塞操作
    print(f"Blocking task {id} finished")

# 顺序执行多个同步任务
def main():
    for i in range(3):
        blocking_task(i)

main()

解释:

在此代码中,每个任务按顺序执行,time.sleep() 会阻塞当前线程,直到所有任务结束。这种顺序执行的方式虽然简单直接,但效率较低,尤其当任务涉及 I/O 操作时,会浪费大量时间。

3 顺序执行异步任务

如果我们希望提高任务的执行效率,可以考虑使用异步任务。异步任务不会阻塞主线程,而是会等待特定的事件(例如 I/O 操作的完成),然后继续执行。

示例代码:

import asyncio

async def async_task(id):
    print(f"Async task {id} started")
    await asyncio.sleep(2)  # 模拟异步操作
    print(f"Async task {id} finished")

# 顺序执行多个异步任务
async def main():
    for i in range(3):
        await async_task(i)

asyncio.run(main())

解释:

通过使用 async def 定义异步函数,await 关键字用于暂停任务的执行并等待异步操作完成。虽然这些任务是异步的,但由于我们使用了 await,它们仍然是顺序执行的。

3 并行执行异步任务

在某些情况下,我们可能希望异步任务能够并行执行,而不是一个接一个地等待。此时可以使用 asyncio.gather(),它允许我们并行运行多个异步任务,从而提高程序效率。

示意图:

示例代码:

import asyncio

async def async_task(id):
    print(f"Async task {id} started")
    await asyncio.sleep(2)
    print(f"Async task {id} finished")

# 并行执行多个异步任务
async def main():
    tasks = [async_task(i) for i in range(3)]
    await asyncio.gather(*tasks)

asyncio.run(main())

解释:

asyncio.gather() 会并行执行多个异步任务,而不是按顺序等待。任务在后台同时运行,极大提高了效率,尤其是当任务需要等待 I/O 时(例如网络请求、文件操作等)。

❗ 注意:

tasks = [async_task(i) for i in range(3)] 这个时候不会执行async_task函数,只是创建协程对象,还没真正的启动。

  • 当你调用 async_task(i) 时,它不会立即执行,而是返回一个 协程对象(coroutine object),这个对象代表一个等待执行的异步任务。
  • 只有当你 await 这个协程对象或者将它传递给 asyncio.gather()、asyncio.create_task()、asyncio.run() 等函数时,协程才会开始执行。

4 并行执行非异步任务(阻塞任务)

如果你有一些外部库提供的阻塞任务(如文件读写、网络操作等),这些任务无法直接变为异步函数。为了与异步任务并行执行这些阻塞任务,asyncio.run_in_executor() 是你的好帮手。

示意图:

示例代码:使用线程池并行执行同步任务

import asyncio
import time
from concurrent.futures import ThreadPoolExecutor

def blocking_task(id):
    print(f"Blocking task {id} started")
    time.sleep(2)
    print(f"Blocking task {id} finished")

async def main():
    with ThreadPoolExecutor() as pool:
        tasks = [
            asyncio.get_event_loop().run_in_executor(pool, blocking_task, i)
            for i in range(3)
        ]
        await asyncio.gather(*tasks)

asyncio.run(main())

解释:

run_in_executor() 将阻塞的任务交给线程池(或进程池)执行,而不会阻塞主事件循环。这使得我们可以同时处理异步任务和阻塞任务。

使用进程池还是线程池?

  • 线程池(ThreadPoolExecutor):适用于 I/O 密集型任务,如文件操作或网络请求。这类任务通常会等待外部事件完成,因此不需要消耗大量 CPU 资源。
  • 进程池(ProcessPoolExecutor):适合 CPU 密集型任务,如数据处理和计算。使用进程池可以充分利用多核 CPU,提升性能。

示例代码:使用进程池

from concurrent.futures import ProcessPoolExecutor
import asyncio

def cpu_intensive_task(id):
    print(f"CPU task {id} started")
    result = sum(i*i for i in range(10**6))  # 模拟CPU密集任务
    print(f"CPU task {id} finished with result: {result}")

async def main():
    with ProcessPoolExecutor() as pool:
        tasks = [
            asyncio.get_event_loop().run_in_executor(pool, cpu_intensive_task, i)
            for i in range(3)
        ]
        await asyncio.gather(*tasks)

asyncio.run(main())

5 总结

  • 顺序执行非异步任务:通常用于简单的任务,但效率低下,容易阻塞线程。
  • 顺序执行异步任务:使用 asyncio 提供的异步函数,能够在等待 I/O 时不阻塞主线程。
  • 并行执行异步任务:通过 asyncio.gather(),可以轻松并行多个异步任务,极大提高执行效率。
  • 并行执行非异步任务:通过 run_in_executor() 将阻塞任务交给线程池或进程池,保证异步任务和同步任务可以并行执行。
  • 线程池 vs 进程池:选择线程池处理 I/O 密集型任务,进程池处理 CPU 密集型任务。

通过这些技巧,你可以在 Python 中轻松管理各种类型的任务,实现高效并行处理。

到此这篇关于Python中asyncio的多种用法的文章就介绍到这了,更多相关Python中asyncio多种用法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python函数中的可变长参数详解

    Python函数中的可变长参数详解

    在本篇文章里小编给大家整理的是关于Python函数中的可变长参数的相关知识点内容,有需要的朋友们参考下。
    2019-09-09
  • python数据可视化之条形图画法

    python数据可视化之条形图画法

    这篇文章主要为大家详细介绍了python数据可视化之条形图画法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • Python利器openpyxl之操作excel表格

    Python利器openpyxl之操作excel表格

    这篇文章主要给大家介绍了关于Python利器openpyxl之操作excel表格的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • python爬虫之利用Selenium+Requests爬取拉勾网

    python爬虫之利用Selenium+Requests爬取拉勾网

    这篇文章主要介绍了python爬虫之利用Selenium+Requests爬取拉勾网,文中有非常详细的代码示例,对正在学习python爬虫的小伙伴们有很好的帮助,需要的朋友可以参考下
    2021-04-04
  • python编写分类决策树的代码

    python编写分类决策树的代码

    这篇文章主要为大家详细介绍了python编写分类决策树的代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • python:pandas合并csv文件的方法(图书数据集成)

    python:pandas合并csv文件的方法(图书数据集成)

    下面小编就为大家分享一篇python:pandas合并csv文件的方法(图书数据集成),具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-04-04
  • Scrapy启动报错invalid syntax的解决

    Scrapy启动报错invalid syntax的解决

    这篇文章主要介绍了Scrapy启动报错invalid syntax的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • Python使用Crypto库实现加密解密的示例详解

    Python使用Crypto库实现加密解密的示例详解

    这篇文章主要为大家详细介绍了Python如何使用Crypto库实现加密解密的功能,文中的示例代码讲解详细,对我们学习Python有一定的帮助,需要的可以参考一下
    2023-01-01
  • django执行原生SQL查询的实现

    django执行原生SQL查询的实现

    本文主要介绍了django执行原生SQL查询的实现,主要有两种方法实现,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • python数据归一化及三种方法详解

    python数据归一化及三种方法详解

    这篇文章主要介绍了python数据归一化及三种方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-08-08

最新评论