当前位置: 首页 > article >正文

Python异步编程模型实战教程

在本教程中,我们介绍Python协程以及如何使用Python async和await关键字来创建和暂停协程。这种异步编程模型在处理大量 I/O 操作(如网络请求、文件读取等)时特别有用,可以避免程序因为等待这些操作而被阻塞。

介绍Python 协程

在编程世界中,执行每个任务需要的时间不同。有的任务可能很快,而有些任务可能需要等待外部资源,例如获取来自服务器的数据或用户输入。作为开发人员,我们经常遇到这样的情况: 我们需要同时执行多个任务,而不是在开始新的任务之前等待前面每个任务都完成。这就是异步编程发挥作用的地方,Python中的asyncio模块是实现这一目标的强大工具。

在传统的同步编程中,任务是顺序地执行,程序等待每个任务完成后再进入下一个任务。在异步编程中,我们可以使用async关键字将某些函数标记为异步,从而允许它们与其他任务并发运行。我们把async标记的函数称为协程,协程可以在运行过程中暂停执行,以实现并发运行多个协程。

要创建和暂停协程,可以使用Python async和await关键字:

  • async关键字创建一个协程。
  • await关键字暂停协程。

定义协程

下面定义了简单的square函数,简单返回整数的平方数:

def square(number: int) -> int:
    return number*number

可以将一个整数作为参数传给square()函数,来获得它的平方数:

def square(number: int) -> int:
    return number*number


result = square(10)
print(result) // 100

当给函数添加async关键字时,该函数将成为协程:

async def square(number: int) -> int:
    return number*number

调用协程返回协程对象,然后打印结果:

async def square(number: int) -> int:
    return number*number


result = square(10)
print(result)

输出如下:

<coroutine object square at 0x00000185C31E7D80>
sys:1: RuntimeWarning: coroutine 'square' was never awaited

在本例中,我们调用square()协程,将返回值赋给result变量,并将其打印出来。当你调用协程时,Python不会立即执行协程内的代码;相反,它仅返回协程对象。

输出中的第二行还显示了一条错误消息,表明从未等待协程。在下面的await部分中可以看到更多:

sys:1: RuntimeWarning: coroutine 'square' was never awaited

Go 语言的协程是轻量级的线程,称为 goroutine,Go 语言的并发模型是基于 CSP(Communicating Sequential Processes,通信顺序进程)模型。Go 语言运行时会自动调度 goroutine 在多个操作系统线程上执行。而Python 的协程基于事件循环模型,通过异步 I/O 和回调来实现并发。要运行协程,需要在事件循环中执行它。在Python 3.7之前,你必须手动创建事件循环来执行协程并关闭事件循环。

然而,从3.7版开始,asyncio库添加了一些简化事件循环管理的函数。例如,您可以使用asyncio.run()函数自动创建事件循环,运行协程并关闭它。下面的代码使用asyncio.run()函数来执行square()协程并获得结果:

import asyncio


async def square(number: int) -> int:
    return number*number

result = asyncio.run(square(10))
print(result)  // 100

特别要注意是,asyncio.run()被设计为asyncio程序的主要入口点。此外,asyncio.run()函数只执行一个协程,该协程可以调用程序中的其他协程。

暂停协程

await关键字暂停协程的执行。await关键字后面是对协程的调用,如下所示:

Result = await my_coroutine()

await关键字导致my_coroutine()执行并等待代码完成、返回结果。要注意,await关键字仅在协程中有效。换句话说,必须在协程中使用await关键字。

这就是为什么在上面的示例中看到的错误消息:需要在协程上使用await关键字。下面的例子展示了如何使用await关键字来暂停协程:

import asyncio

async def square(number: int) -> int:
    return number*number

async def main() -> None:
    x = await square(10)
    print(f'x={x}')

    y = await square(5)
    print(f'y={y}')

    print(f'total={x+y}')

if __name__ == '__main__':
    asyncio.run(main())

输出结果:

x=100
y=25
total=125

它是如何工作的。我们将重点关注main()函数 :

首先,使用await关键字调用square()协程。await关键字将暂停main()协程的执行,等待square()协程完成,并返回结果:

x = await square(10)
print(f'x={x}')

其次,使用await关键字第二次调用square()协程:

y = await square(5) 
print(f' y={y}')

第三,显示总数:

print(f 'total = {x + y}')

下面的语句使用run()函数来执行main()协程并管理事件循环:

asyncio.run(main())

到目前为止,我们的程序像同步程序一样执行。它没有揭示并发模型的强大功能,但通过简单示例让你更清晰掌握异步任务创建、执行。

完整示例

以下是一个更详细的 Python 中asyncawait关键字的示例及解释:

import asyncio

async def slow_operation(name, duration):
    print(f"Starting {name}...")
    await asyncio.sleep(duration)
    print(f"{name} completed.")
    return f"{name} result"

async def main():
    task1 = slow_operation("Task 1", 2)
    task2 = slow_operation("Task 2", 3)

    # 同时执行两个异步任务
    results = await asyncio.gather(task1, task2)
    print(results)

asyncio.run(main())
  • async def定义了一个异步函数。在这个例子中,slow_operationmain都是异步函数。
  • slow_operation模拟一个耗时的操作,这里使用asyncio.sleep来暂停执行指定的时间。当遇到await asyncio.sleep(duration)时,这个异步函数会暂停执行,将控制权交还给事件循环,让事件循环可以去执行其他的任务。
  • main函数中,首先创建了两个异步任务task1task2,分别调用slow_operation。然后使用asyncio.gather来同时执行这两个任务,并等待它们全部完成。asyncio.gather会返回一个包含所有任务结果的列表。
  • 最后,使用asyncio.run(main())来运行主异步函数,它会创建一个新的事件循环并在其中运行main函数,直到main函数完成后关闭事件循环。

场景实战

前面示例仅为模拟任务,下面是使用asyncawait的实际场景示例,。这个示例展示如何在等待多个网页下载的同时,不会阻塞程序的执行,可以提高程序的效率。

import asyncio
import aiohttp

async def download_page(session, url):
    """
    异步函数,用于下载指定 URL 的网页内容。
    参数:
    - session:aiohttp 的客户端会话对象。
    - url:要下载的网页 URL。
    """
    async with session.get(url) as response:
        if response.status == 200:
            return await response.text()
        else:
            return None

async def main():
    """
    主异步函数,创建任务列表并等待所有任务完成。
    """
    urls = [
        "https://www.example.com",
        "https://www.example.org",
        "https://www.example.net"
    ]
    async with aiohttp.ClientSession() as session:
        tasks = [download_page(session, url) for url in urls]
        pages = await asyncio.gather(*tasks)
        for url, page in zip(urls, pages):
            if page:
                print(f"Downloaded {url} successfully.")
            else:
                print(f"Failed to download {url}.")

if __name__ == "__main__":
    asyncio.run(main())

解释:

  • download_page函数接受一个aiohttp的会话对象和一个 URL 作为参数。使用async with语句创建一个异步的 HTTP GET 请求。如果响应状态码是 200,表示请求成功,就使用await response.text()获取网页内容并返回。如果状态码不是 200,则返回None
  • main函数中定义了要下载的网页 URL 列表。使用aiohttp.ClientSession创建一个客户端会话对象,这个对象可以在多个请求之间复用连接以提高性能。
  • 通过列表推导式创建一个任务列表,每个任务都是调用download_page函数,传入不同的 URL 和会话对象。
  • 使用asyncio.gather同时执行所有任务,并等待它们全部完成。asyncio.gather会返回一个包含所有任务结果的列表,按照任务的调用顺序排列。
  • 最后,遍历 URL 和对应的网页内容,如果内容不为None,表示下载成功,打印成功信息;否则,打印下载失败信息。

总结

  • 协程是一种常规函数,它能够暂停当前正在执行的任务,去执行其他长时间运行的操作、等待结果,并从暂停点恢复。
  • 使用async关键字定义协程,使用await关键字暂停协程。使用asyncio.run()函数在事件循环上自动执行协程并管理事件循环。

总之,使用asyncawait关键字可以编写异步代码,使得程序在等待某些耗时操作时可以继续执行其他任务,提高程序的效率和响应性。


http://www.kler.cn/news/333980.html

相关文章:

  • JavaSE——面向对象练习题
  • CSS实现磨砂玻璃效果
  • 031集——文本文件按空格分行——C#学习笔记
  • 【Android】初级控件
  • 栈的介绍与实现
  • 5G上的时敏网络:带有IEEE 802.1Qbv流量的混合5G和TSN系统的实验评估
  • freex源码抄写+ue5视频2个+渲染泛读催眠
  • 已解决-Nacos明明成功运行,但Spring报错连接不上
  • 沂机管理系统/data/Ajax.aspx接口存在SQL注入漏洞
  • C++ | Leetcode C++题解之第452题用最少数量的箭引爆气球
  • Linux基于CentOS学习【进程状态】【进程优先级】【调度与切换】【进程挂起】【进程饥饿】
  • C语言自定义类型联合和枚举(25)
  • 虾皮Shopee Android面试题及参考答案
  • 新手教学系列——MacOS 10.13.6下如何使用curl_cffi模拟Chrome请求
  • 【AIGC】ChatGPT提示词Prompt解析:如何打造个人IP、CSDN爆款技术文案与高效教案设计
  • mysql学习教程,从入门到精通,SQL LIKE 运算符(28)
  • 深入理解Dubbo源码核心原理-Part4
  • 7.Javaweb-Ajax
  • 计算机毕业设计 网上体育商城系统的设计与实现 Java实战项目 附源码+文档+视频讲解
  • 计算机毕业设计 基于爬虫与文本挖掘的网络舆情监控系统的设计与实现 Python+Django+Vue 前后端分离 附源码 讲解 文档