跳至主要內容

18. 异步编程与协程

小白debug大约 5 分钟

18. 异步编程与协程

今天我们将探讨一个非常有趣的主题: 异步编程与协程

在我们开始之前,让我们保持轻松,别担心,这个话题虽然听起来有点高级,但它可以让你的程序变得更加高效!

什么是异步编程?

首先,让我们了解一下异步编程。在传统的同步编程中,一行代码执行完后才能执行下一行。但在异步编程中,程序可以在等待某些操作完成的同时,继续执行其他任务。

这样的好处在于,我们可以充分利用等待时间,让 CPU 在等待 I/O 操作时去处理其他任务,从而提高程序的整体效率。

为什么需要异步编程?

假设你在下载文件时,如果采用同步的方式,程序会一直等待文件下载完成才能继续执行其他操作。但如果你使用异步编程,可以在等待文件下载的同时,做其他事情,比如处理其他网络请求或响应用户操作。

什么是协程?

协程是异步编程的一种方式,它允许我们在函数内部通过 await 关键字等待其他协程的执行结果,而不会阻塞整个程序。

使用 asyncio 进行异步编程

Python 提供了一个内置的库 asyncio 来支持异步编程。下面让我们通过一个例子来学习如何使用 asyncio

import asyncio

async def say_hello():
    print('Hello')
    await asyncio.sleep(1)
    print('World')

# 创建一个事件循环
loop = asyncio.get_event_loop()

# 将协程放入事件循环中执行
loop.run_until_complete(say_hello())

# 关闭事件循环
loop.close()

在这个例子中,我们首先定义了一个协程 say_hello,它会打印 "Hello",然后等待一秒钟,最后打印 "World"。在协程内部,我们使用 await 关键字来等待异步操作的结果。

异步编程的使用场景

异步编程特别适合处理 I/O 密集型任务,比如网络请求、文件读写等。当程序在等待外部操作完成的时候,可以继续执行其他任务,从而提高了程序的效率。

使用协程实现异步编程

让我们通过一个例子来看看如何使用协程实现异步编程:

import asyncio

async def fetch_data(url):
    print(f'开始下载:{url}')
    await asyncio.sleep(2)  # 模拟网络请求
    print(f'下载完成:{url}')

async def main():
    tasks = [
        fetch_data('https://example.com/image1.jpg'),
        fetch_data('https://example.com/image2.jpg'),
        fetch_data('https://example.com/image3.jpg')
    ]
    await asyncio.gather(*tasks)

# 运行主协程
asyncio.run(main())

在这个例子中,我们定义了一个协程 fetch_data,它模拟了一个网络请求的过程。然后在 main 函数中,我们创建了多个任务,使用 asyncio.gather 来并发执行这些任务。

异步编程 vs. 多线程

协程和线程有一个重要的区别。线程是由操作系统调度的,它们在不同的 CPU 核心上执行。而协程是在一个线程内部执行的,它由程序员手动控制。这意味着协程不会涉及到线程切换的开销,因此通常比多线程更高效。

为什么要考虑使用协程?

使用线程会涉及到锁、信号量等线程同步机制,这会增加代码的复杂度。而使用协程,我们可以避免这些问题,让程序更加简洁清晰。

GIL 锁是什么?

GIL(全局解释器锁)是 Python 解释器中的一个机制,它确保同一时刻只有一个线程在执行 Python 字节码。这意味着在多线程环境下,Python 解释器无法利用多核处理器的优势。

为什么协程不受 GIL 的影响?

由于协程是在一个线程内部执行的,并且程序员可以自由控制协程的切换,所以它们不受 GIL 的限制。这使得协程成为 Python 中处理并发的强大工具。

通过本文,我们学习了异步编程与协程的基本概念,以及如何使用 asyncio 来实现异步编程。异步编程可以在等待某些操作的同时,继续执行其他任务,从而提高程序的整体效率。

同时,我们还介绍了如何使用协程来实现异步编程,并通过一个实际的例子演示了异步下载图片的过程。

希望你现在对异步编程与协程有了更清晰的理解。继续加油,你已经掌握了一个非常有用的 Python 编程技能!

实战例子:异步下载图片

让我们通过一个实际的例子来巩固所学吧!假设我们需要从网上下载一批图片,我们可以使用异步编程来同时下载,以节省时间。

import asyncio
import aiohttp

async def download_image(session, url, filename):
    async with session.get(url) as response:
        with open(filename, 'wb') as file:
            file.write(await response.read())
        print(f'{filename} 下载完成')

async def main():
    async with aiohttp.ClientSession() as session:
        tasks = [
            download_image(session, 'https://example.com/image1.jpg', 'image_1.jpg'),
            download_image(session, 'https://example.com/image2.jpg', 'image_2.jpg'),
            download_image(session, 'https://example.com/image3.jpg', 'image_3.jpg')
        ]
        await asyncio.gather(*tasks)

# 运行主协程
asyncio.run(main())

通过本文,我们学习了异步编程与协程的基本概念,以及如何使用 asyncio 来实现异步编程。异步编程可以在等待某些操作的同时,继续执行其他任务,从而提高程序的整体效率。

同时,我们还介绍了如何使用协程来实现异步编程,并通过一个实际的例子演示了异步下载图片的过程。

希望你现在对异步编程与协程有了更清晰的理解。继续加油,你已经掌握了一个非常有用的 Python 编程技能!

训练营看视频