Python 协程入门到实践
Python 协程是实现异步编程的一种重要机制。它能够在单线程中实现并发操作,避免了线程的切换开销,适用于 I/O 密集型任务。Python 的协程主要通过 asyncio
模块来管理,结合 async
和 await
关键字,可以让代码在等待 I/O 操作时不阻塞其他任务。
什么是协程?
协程是一种特殊的函数,它能够在执行过程中暂停,并在未来某个时间恢复执行。协程函数由 async def
定义,调用时返回一个协程对象。
协程的基本特性
- 异步执行:协程可以在等待 I/O 操作时释放控制权,让其他任务运行。
- 协作式多任务:协程通过
await
关键字主动让出控制权,执行其他任务,减少了上下文切换的开销。
协程的创建与使用
创建协程函数
1 2 3 4 5 6 7
| import asyncio
async def say_hello(): print("Hello, World!")
|
执行协程
1 2 3 4
|
asyncio.run(say_hello())
|
在这段代码中,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) 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 会等待所有任务完成。
输出:
虽然 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())
|
输出:
在并发任务中,如果发生异常,可以通过 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())
|
输出:
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', ] 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 密集型任务时。