Python 协程入门到实践

Python 协程入门到实践

Python 协程是实现异步编程的一种重要机制。它能够在单线程中实现并发操作,避免了线程的切换开销,适用于 I/O 密集型任务。Python 的协程主要通过 asyncio 模块来管理,结合 asyncawait 关键字,可以让代码在等待 I/O 操作时不阻塞其他任务。

什么是协程?

协程是一种特殊的函数,它能够在执行过程中暂停,并在未来某个时间恢复执行。协程函数由 async def 定义,调用时返回一个协程对象。

协程的基本特性

  1. 异步执行:协程可以在等待 I/O 操作时释放控制权,让其他任务运行。
  2. 协作式多任务:协程通过 await 关键字主动让出控制权,执行其他任务,减少了上下文切换的开销。

协程的创建与使用

创建协程函数

1
2
3
4
5
6
7

import asyncio

# 使用 async 定义协程函数
async def say_hello():
print("Hello, World!")

执行协程

1
2
3
4

# 通过 asyncio.run 来启动协程
asyncio.run(say_hello()) # 输出: Hello, World!

在这段代码中,asyncio.run 会运行一个事件循环并执行协程。

异步 I/O 操作

协程的主要用途是执行异步 I/O 操作,通常涉及到网络请求、文件读取等任务。使用 await 可以等待一个耗时操作完成而不阻塞其他任务。

异步IO实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import asyncio

async def fetch_data():
print("开始获取数据...")
await asyncio.sleep(2) # 模拟网络请求,等待 2 秒
print("数据获取完毕")
return "数据内容"

async def main():
print("任务开始")
data = await fetch_data()
print(f"获取到的数据: {data}")

# 运行主任务
asyncio.run(main())

在这个例子中,fetch_data 是一个模拟异步 I/O 操作的协程,它通过 await asyncio.sleep(2) 来模拟等待网络请求的时间。

输出:

1
2
3
4
5
6

任务开始
开始获取数据...
数据获取完毕
获取到的数据: 数据内容

注意到,尽管 fetch_data 内部有 await 操作,main 协程会等到 fetch_data 完成之后才继续执行。

协程任务并发执行

如果你需要同时执行多个协程任务,可以使用 asyncio.gather 来并发执行多个协程。

多个协程并发执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import asyncio

async def task_1():
await asyncio.sleep(1)
print("任务 1 完成")

async def task_2():
await asyncio.sleep(2)
print("任务 2 完成")

async def main():
await asyncio.gather(task_1(), task_2())

asyncio.run(main())

这里,task_1 和 task_2 被并发执行。asyncio.gather 会等待所有任务完成。

输出:

1
2
3
任务 1 完成
任务 2 完成

虽然 task_2 花费了 2 秒,而 task_1 只花费了 1 秒,但由于是并发执行的,最终任务只花费了 2 秒。

异常处理

在协程中,如果某个任务出现异常,通常会被 try…except 语句捕获。但需要注意的是,异常处理可能会影响到其他并发执行的任务。

协程中的异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import asyncio

async def task_with_exception():
await asyncio.sleep(1)
raise Exception("任务发生了异常")

async def main():
try:
await asyncio.gather(task_with_exception())
except Exception as e:
print(f"捕获到异常: {e}")

asyncio.run(main())

输出:

1
捕获到异常: 任务发生了异常

在并发任务中,如果发生异常,可以通过 asyncio.gather 的 return_exceptions=True 参数来忽略异常,继续执行其他任务。

捕获所有异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import asyncio

async def task_with_exception():
await asyncio.sleep(1)
raise Exception("任务发生了异常")

async def main():
result = await asyncio.gather(
task_with_exception(),
return_exceptions=True
)
print(result)

asyncio.run(main())

输出:

1
2
[Exception('任务发生了异常')]

asyncio.gather 将会返回一个包含所有任务结果的列表,若任务有异常,则返回异常对象。

进阶示例:模拟 HTTP 请求

我们可以使用协程来模拟异步的 HTTP 请求,利用 aiohttp 库来实现。

使用 aiohttp 实现并发 HTTP 请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import aiohttp
import asyncio

async def fetch_url(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
print(f"读取 {url} 完成,状态码: {response.status}")
return await response.text()

async def main():
urls = [
'https://www.baidu.com',
'https://www.python.org',
'https://www.github.com',
]

# 并发地请求多个 URL
tasks = [fetch_url(url) for url in urls]
await asyncio.gather(*tasks)

asyncio.run(main())

这个例子中,我们使用 aiohttp 库发送 HTTP GET 请求,并且使用 asyncio.gather 来并发地执行多个请求。

总结

1.协程 是实现异步 I/O 操作的一种方法,通过 async 和 await 可以让程序在等待某些操作时不会阻塞,达到高效执行的目的。

2.asyncio 模块 提供了事件循环和任务调度的机制,使得协程可以方便地并发执行。

3.asyncio.gather 可以用于并发多个任务,await 用来等待任务的完成。

4.协程不仅可以提升性能,还能使代码更加简洁与易于理解,尤其是在面对大量 I/O 密集型任务时。


Python 协程入门到实践
https://dreamshao.github.io/2025/02/08/python协程/
作者
Yun Shao
发布于
2025年2月8日
许可协议