Python使用asyncio包实现异步编程方式

简介: 异步编程是一种编程范式,用于处理程序中需要等待异步操作完成后才能继续执行的情况。异步编程允许程序在执行耗时的操作时不被阻塞,而是在等待操作完成时继续执行其他任务。这对于处理诸如文件 I/O、网络请求、定时器等需要等待的操作非常有用。

一、异步编程

异步编程是一种编程范式,用于处理程序中需要等待异步操作完成后才能继续执行的情况。


异步编程允许程序在执行耗时的操作时不被阻塞,而是在等待操作完成时继续执行其他任务。


这对于处理诸如文件 I/O、网络请求、定时器等需要等待的操作非常有用。



使用异步编程通常可以带来以下好处:

  • 提高程序效率和性能:异步编程使得程序在执行耗时的 I/O 操作(如网络请求、文件读写、数据库查询等)时不会被阻塞,减少了等待时间,充分利用了系统资源。
  • 改善用户体验:在 Web 开发中,异步编程可以确保服务器在处理大量并发请求时能够快速地响应用户,从而提高了 Web 应用的响应速度和用户体验。

二、async/await和asyncio包

2.1 异步函数的定义

在Python中实现异步函数的定义需要两个关键字(async和await)。

  • async:async关键字声明一个异步函数。它可以在执行过程中暂停并允许其他代码执行。当你调用一个异步函数时,它会立即返回一个协程对象而不是实际的结果。异步函数适用于执行耗时的I/O操作,例如网络请求、文件读写、数据库查询等。这些操作通常涉及到等待外部资源的响应或者数据的传输,而在等待的过程中,CPU可以执行其他任务,从而提高程序的效率。
  • await:await关键字在Python中用于等待一个异步操作完成。当调用异步函数时,使用await关键字可以暂时挂起当前的异步函数的执行,将CPU控制权还给事件循环(Event Loop)。接着事件循环可以将执行权转移到其他任务上,而不是一直等待当前的异步函数完成。当被await的异步操作完成后,事件循环会通知原来的异步函数,使得它可以继续执行后续的操作。


在Python中异步函数的定义需要同时满足以下两个条件:

  • 使用async def关键字声明函数。
  • 函数内部包含异步操作,并且使用了await关键字等待异步操作完成。如果一个函数中只使用了async def声明,但其中任何异步操作,也没有使用await关键字,那么它实际上就是一个普通的同步函数,而不是一个异步函数。

2.2 事件循环

事件循环(Event Loop)是异步编程中负责管理和调度异步任务执行的机制。


事件循环的工作原理类似于一个持续运行的循环,它在每一轮循环中都会执行以下几个步骤:

  • 等待任务就绪: 事件循环会等待所有注册的异步任务就绪,包括等待 I/O 操作完成、等待计时器超时等。
  • 选择就绪任务:一旦有任务就绪,事件循环会选择其中一个任务进行执行。
  • 执行任务:事件循环会执行所选择的就绪任务,直到任务完成或者遇到await关键字,需要暂时挂起任务的执行。
  • 挂起任务:如果任务遇到await关键字,它会将控制权交还给事件循环,并等待 await后面的异步操作完成。
  • 继续执行其他任务:在等待await的异步操作完成的过程中,事件循环会继续执行其他就绪的任务,从而实现了并发执行的效果。
  • 异步操作完成: 当一个 await 后面的异步操作完成后,事件循环会通知原来的任务,使得它可以继续执行后续的操作。

2.3 asyncio包

asyncio包python中常用的异步编程框架,这里使用该框架完成一个简单的异步编程案例,具体如下:


import time
import datetime
import asyncio
async def async_read_file():
    print("async读文件开始:",datetime.datetime.fromtimestamp(time.time()))
    await asyncio.sleep(20)
    print("async读文件完成:",datetime.datetime.fromtimestamp(time.time()))
def computer():
    print("普通计算密集型任务:",datetime.datetime.fromtimestamp(time.time()))
    sum=0
    for i in range(1000000):
        if i%250000==0 and i!=0:
            print("普通计算密集型任务正在执行:",datetime.datetime.fromtimestamp(time.time()))
        for j in range(500):
            sum+=i+j-2*j
    print("普通计算密集型任务完成:",datetime.datetime.fromtimestamp(time.time()))
def computer2():
    print("普通CPU密集型任务:",datetime.datetime.fromtimestamp(time.time()))
    sum=0
    for i in range(1000000):
        if i%250000==0 and i!=0:
            print("普通CPU密集型任务正在执行:",datetime.datetime.fromtimestamp(time.time()))
        for j in range(5000):
            sum+=i+j-2*j
    print("普通CPU密集型任务完成:",datetime.datetime.fromtimestamp(time.time()))
async def asy_main():
    task=loop.create_task(async_read_file()) # 创建一个任务,并添加到事件循环,等待执行
    task2=loop.run_in_executor(None,computer)# 将普通函数read_file添加到事件循环中,等待执行
    task3=loop.run_in_executor(None,computer2)# 将普通函数read_file2添加到事件循环中,等待执行
    await task3
    await task2
    await task
loop=asyncio.get_event_loop() # 创建一个事件循环
loop.run_until_complete(asy_main())


其执行结果如下:


普通计算密集型任务: 2024-05-15 18:29:19.702689

普通CPU密集型任务: 2024-05-15 18:29:19.708280

async读文件开始: 2024-05-15 18:29:19.738654

普通计算密集型任务正在执行: 2024-05-15 18:29:21.441072

普通计算密集型任务正在执行: 2024-05-15 18:29:23.192585

普通计算密集型任务正在执行: 2024-05-15 18:29:24.936979

普通计算密集型任务完成: 2024-05-15 18:29:26.712930

普通CPU密集型任务正在执行: 2024-05-15 18:29:32.539679

async读文件完成: 2024-05-15 18:29:39.752731

普通CPU密集型任务正在执行: 2024-05-15 18:29:41.813872

普通CPU密集型任务正在执行: 2024-05-15 18:29:51.103737

普通CPU密集型任务完成: 2024-05-15 18:30:00.433402


从代码运行结果中可以看到,两个计算密集型的任务task2、task3和异步函数task添加到事件循环上之后,在等待异步操作task完成的过程中,CPU并没有闲着,而是在执行task2和task3的任务。


Tips:虽然当下的执行结果中写完成了computer()的计算,后完成了computer2()的计算,但多次执行上述程序的时候也出现了两个函数交替执行的结果。

为了与上述代码形成对比,执行下述代码:


import asyncio
import datetime
async def async_task(name, delay):
    print(f"Task {name} started:",datetime.datetime.now())
    await asyncio.sleep(delay)
    print(f"Task {name} finished:",datetime.datetime.now())
async def main():
    await async_task("A", 2)
    await async_task("B", 1)
    await async_task("C", 3)
asyncio.run(main())


其代码执行结果如下:


Task A started: 2024-05-21 17:45:24.324535

Task A finished: 2024-05-21 17:45:26.326109

Task B started: 2024-05-21 17:45:26.326250

Task B finished: 2024-05-21 17:45:27.327795

Task C started: 2024-05-21 17:45:27.327923

Task C finished: 2024-05-21 17:45:30.329475


从执行结果上可以看到这三个异步操作是顺序执行的,并没有同时执行。


这是因为在执行await后面的异步操作时事件循环中只有一个任务。

相关文章
WK
|
15天前
|
Python
如何在Python中导入包
在 Python 中,包是一种组织代码的方式,通过包含 `__init__.py` 文件(在 Python 3.3 及以上版本可选)的目录实现。包内可以包含多个模块(`.py` 文件)和其他子包。导入包有多种方式:整体导入包、导入特定模块、导入特定函数或类、导入子包等。推荐的做法是明确指定导入内容以提高代码的可读性和可维护性。此外,确保包目录结构正确,并将其添加到 Python 的搜索路径中。对于分发包,使用 setuptools 和 pip 等工具更为便捷。
WK
111 66
|
15天前
|
数据采集 API 数据库
探索Python中的异步编程:从理解到实践
【8月更文挑战第30天】在Python世界中,异步编程是一个既神秘又强大的概念。它像是给程序装上了翅膀,让原本缓慢、阻塞的操作变得迅速而流畅。本文将带你走进异步编程的世界,从基本的概念讲起,通过实例演示如何运用Python的异步特性来提升程序的性能和响应速度。我们将一步步构建一个简易的异步Web爬虫,让你在实践中感受异步编程的魅力。
WK
|
15天前
|
Python
如何在Python中创建包
在Python中创建包十分简便,主要涉及目录结构的设置及`__init__.py`文件的配置。虽然Python 3.3后空`__init__.py`文件不再强制要求,但在特定场景下保留它有助于保持兼容性或执行包初始化代码。创建包的具体步骤包括:构建目录结构、编写模块代码、(可选)编写初始化代码等。例如,可以创建一个名为`mypackage`的目录,其中包含`__init__.py`及多个模块文件如
WK
106 62
|
4天前
|
数据采集 开发者 Python
探索Python中的异步编程:从基础到实战
【9月更文挑战第9天】本文将带你进入Python异步编程的世界,从理解其核心概念开始,逐步深入到实际应用。我们将一起构建一个小型的异步Web爬虫,通过实践学习如何在不阻塞主线程的情况下并发处理任务,优化程序性能。文章不仅包含理论知识,还提供代码示例,让读者能够动手实践,深刻理解异步编程的力量。
26 12
|
9天前
|
数据采集 Python
探索Python中的异步编程:从基础到实战
【9月更文挑战第4天】在Python的海洋中,异步编程犹如一艘快艇,让你的代码在执行效率和响应速度上破浪前行。本文将带你从理解“异步”这一概念出发,深入到Python的asyncio库的使用,再到构建一个实际的异步Web爬虫项目,体验异步编程的魅力。我们将避开枯燥的理论,通过生动的比喻和直观的代码示例,让异步编程的知识活灵活现。
|
14天前
|
设计模式 数据库 开发者
探索Python中的异步编程:从理解到实践
【8月更文挑战第31天】本文旨在通过浅显易懂的语言和具体代码示例,为读者揭开Python异步编程的神秘面纱。我们将从基础概念入手,逐步深入到实战应用,让读者能够不仅理解异步编程的内涵,还能掌握其在实际项目中的应用技巧。无论你是编程新手还是有经验的开发者,这篇文章都将为你提供有价值的见解和指导。
|
15天前
|
设计模式 调度 Python
Python中的异步编程:从理解到实践打造你的个人博客——从零开始的指南
【8月更文挑战第30天】本文将带你深入探索Python的异步编程世界,从基础概念到实际应用,一步步揭示如何通过asyncio库提升程序的响应性和效率。我们将通过实际代码示例,展示如何创建异步任务、管理事件循环以及处理并发操作,让你的代码运行得更加流畅和高效。
|
14天前
|
设计模式 调度 开发者
探索Python中的异步编程:从基础到实战
【8月更文挑战第31天】本文将带你进入Python异步编程的世界,通过浅显易懂的语言和实例,让你理解异步编程的概念、优势及应用场景。我们将一起学习asyncio库的基础用法,并通过代码示例深入探讨异步IO操作、任务管理以及异常处理等关键知识点。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开一扇通往高效异步编程的大门。
|
14天前
|
数据采集 数据处理 开发者
探索Python中的异步编程:从基础到高级
【8月更文挑战第31天】在数字世界中,速度和效率至关重要。Python的异步编程提供了一种机制,使得程序可以同时处理多个任务,而无需等待某个任务完成。本文将带你了解异步编程的概念,并通过实际代码示例,展示如何在Python中实现异步功能。我们将一起构建一个简单的异步Web爬虫,以实践理论知识并体验异步带来的性能提升。