【asyncio实战教程】:一步到位学会协程创建与管理
发布时间: 2024-10-02 05:02:44 阅读量: 23 订阅数: 31
![python库文件学习之asyncio](https://d2ms8rpfqc4h24.cloudfront.net/working_flow_of_node_7610f28abc.jpg)
# 1. asyncio入门与异步编程概念
在这个信息时代,快速、高效是软件开发追求的目标之一。Python作为一门广泛使用的编程语言,其异步编程库`asyncio`,能够帮助开发者编写出能同时处理多任务的高效程序。在第一章中,我们将介绍asyncio的基本概念,以及异步编程的核心思想,为读者步入更深入的异步世界打下坚实基础。
## 1.1 异步编程的必要性
随着系统复杂度的增加和对性能要求的提升,传统的多线程编程由于线程开销大,锁竞争等问题,已不能满足所有的需求。异步编程允许程序在等待I/O操作完成时,不阻塞主程序流,而是继续执行其他任务,这样能够显著提高程序效率,减少资源消耗。
## 1.2 asyncio库的作用与优势
asyncio库是Python中的一个用于异步编程的库,它提供了一种编写单线程并发代码的方式。它支持协程(coroutines),并内置了事件循环来管理并发执行的协程。通过asyncio,开发者可以避免复杂的多线程或多进程代码,同时享受异步编程带来的性能优势。
## 1.3 异步编程的基本术语
在继续之前,我们需要了解一些异步编程的基本术语:
- **协程(Coroutines)**: 一个轻量级线程,是一种非抢占式的上下文切换方式,可以看作是函数的扩展,支持挂起和恢复。
- **事件循环(Event Loop)**: 负责在多个协程之间调度执行。
- **异步函数(async def)**: 用于定义协程的语法结构,通过`async`关键字标识。
- **等待对象(Awaitable)**: 可以被`await`操作的异步对象,例如协程或Future对象。
```python
import asyncio
async def main():
print("Hello")
await asyncio.sleep(1)
print("World")
# 运行事件循环
asyncio.run(main())
```
以上代码展示了最简单的asyncio程序,通过`async def`定义了一个协程,并使用`await`来等待异步函数`asyncio.sleep`的完成。这是异步编程世界的入口,接下来我们将一起探索这个世界的奇妙之处。
# 2. 深入理解asyncio核心组件
在异步编程的世界中,理解asyncio的核心组件至关重要。本章节将深入探讨Event Loop的工作原理与应用、协程的创建与控制流,以及Future与Task的协作机制。
## 2.1 Event Loop的原理与应用
### 2.1.1 Event Loop的概念
Event Loop是异步编程模型的核心,它是驱动整个程序的主循环。在asyncio中,Event Loop负责管理和调度所有的协程。每个协程都可以被暂停,等待某个事件的发生,而Event Loop则会在适当的时机恢复这些协程的执行。
Event Loop循环的每个阶段都可能执行不同类型的操作:
1. **执行回调**:处理完成的回调函数。
2. **等待I/O**:等待I/O事件发生。
3. **执行子进程回调**:子进程完成时调用的回调。
4. **超时回调**:定时器事件。
### 2.1.2 事件循环的生命周期
Event Loop的生命周期从启动开始,会经历多个阶段,直到被明确地停止。以下是一个简化的生命周期流程图:
```mermaid
graph TD
A[启动Event Loop] --> B[处理当前任务]
B --> C{任务队列是否为空?}
C -- 是 --> D[等待事件]
C -- 否 --> E[执行任务]
D --> F{是否有事件发生?}
F -- 是 --> E
F -- 否 --> B
E --> G[处理回调]
G --> H{是否要停止Loop?}
H -- 是 --> I[关闭所有连接]
H -- 否 --> B
I --> J[Event Loop停止]
```
在这个过程中,Loop会持续等待新的事件,比如IO完成、定时器触发或者新的任务提交。
## 2.2 协程的创建与控制流
### 2.2.1 使用async定义协程
在Python中,使用`async def`可以定义一个协程,它是一个特殊的函数,可以挂起和恢复执行,而不会阻塞线程。例如:
```python
import asyncio
async def my_coroutine():
await asyncio.sleep(1)
print('Hello from a coroutine!')
```
上述代码定义了一个简单的协程,使用`await`暂停并等待`asyncio.sleep`这个异步操作完成。
### 2.2.2 yield from与await的对比
`yield from`和`await`在Python中用于处理异步操作,但它们在语法和用法上有明显的不同。`yield from`是Python 3.3之前用于协程内部调用另一个生成器的方法,而在Python 3.5及以后,`await`是推荐的方式用于替代`yield from`,专门用于异步编程。
```python
# yield from 示例
def gen():
yield from asyncio.sleep(1)
async def main():
async for _ in gen():
pass
# await 示例
async def coro():
await asyncio.sleep(1)
```
`await`只能在`async`定义的协程中使用,它会暂停当前协程直到等待的Future或Task完成。
### 2.2.3 协程的暂停与恢复机制
协程的暂停与恢复是通过Python的`asyncio`库中的`Task`对象实现的。`Task`对象负责协程的调度,当协程执行到`await`时,`Task`会被挂起,等待某个事件完成之后,`Task`负责恢复该协程的执行。
```python
async def my_coroutine():
print('协程开始')
await asyncio.sleep(1) # 协程在此处暂停
print('协程结束')
task = asyncio.create_task(my_coroutine())
task.result() # 等待任务完成
```
这个过程可以看做是协程和Event Loop之间的交互,Event Loop负责在适当的时候激活和暂停任务。
## 2.3 Future与Task的协作
### 2.3.1 Future对象的工作原理
`Future`是`asyncio`库中最小的执行单元,可以看作是一个待处理的操作的结果容器。`Future`对象通常由库的内部代码创建,但也可以在开发者代码中创建。`Future`是协作性对象,它表示一个最终会完成的操作。
```python
import asyncio
future = asyncio.Future()
def set_future_done(future):
# 稍后将Future设置为完成状态
loop = asyncio.get_running_loop()
loop.call_soon_threadsafe(future.set_result, 42)
asyncio.ensure_future(set_future_done(future))
await future # 等待Future完成
print(future.result()) # 输出结果 42
```
在上面的代码中,`Future`对象被用作一个结果的容器,它最终会被设置为完成状态,并且可以在协程中等待其完成。
### 2.3.2 Task对象在并发中的作用
`Task`是基于`Future`的封装,它包装了一个协程,并提供了一个`Future`对象完成的接口。`Task`自动将协程运行结果与`Future`关联起来,当协程结束时,`Task`也随之完成。
```python
async def my_coroutine(x):
await asyncio.sleep(1)
return x + 10
task = asyncio.create_task(my_coroutine(5))
result = await task # 等待Task完成
print(result) # 输出结果 15
```
`Task`对异步编程非常有用,因为它使得多个异步操作可以并行运行,而无需阻塞执行。
### 2.3.3 异步任务的取消和超时处理
在长时间运行的异步程序中,取消任务和设置超时是常见的需求。在`asyncio`中,可以使用`Task.cancel()`来请求取消任务,而`asyncio.wait_for`可以用于设置超时。
```python
async def main():
try:
await asyncio.wait_for(my_coroutine(5), timeout=1.0)
except asyncio.TimeoutError:
print('协程执行超时')
task = asyncio.create_task(main())
```
在上述代码中,`asyncio.wait_for`包装了`my_coroutine`函数,并设置了1秒的超时时间。如果任务在指定时间内没有完成,将抛出`asyncio.TimeoutError`。
通过这些核心组件的深入理解,我们可以更好地掌握asyncio的工作原理,并有效地应用到实际的异步编程实践中。在下一章节中,我们将进一步探讨asyncio在实战中的技巧与模式。
# 3. asyncio实战技巧与模式
## 异步网络编程
### 使用asyncio创建TCP/UDP客户端和服务器
在使用asyncio进行网络编程时,创建TCP/UDP客户端和服务器是基础且重要的操作。利用asyncio的网络库,我们能够快速搭建起基于事件循环的异步网络服务。
#### TCP/UDP服务器
要创建一个异步TCP/UDP服务器,通常会用到`asyncio.start_server`函数。下面是一个异步TCP服务器的简单例子:
```python
import asyncio
async def handle_client(reader, writer):
data = await reader.read(100)
message = data.decode()
addr = writer.get_extra_info('peername')
print(f"Received {message!r} from {addr!r}")
print(f"Send: {message!r}")
writer.write(data)
await writer.drain()
print("Closing the connection")
writer.close()
async def main():
server = await asyncio.start_server(
handle_client, '***.*.*.*', 8888)
addr = server.sockets[0].getsockname()
print(f'Serving on {addr}')
async with server:
await server.serve_forever()
asyncio.run(main())
```
在上述代码中,我们定义了`handle_client`协程函数来处理传入的连接。一旦客户端连接,它会读取数据,并将数据原样回传给客户端。
#### TCP/UDP客户端
对于客户端,我们使用`asyncio.open_connection`来建立连接,然后进行数据的读写操作:
```python
import asyncio
async def main():
reader, write
```
0
0