Python中使用yield实现协程调度
已于 2025年01月20日 17:50 修改
访问次数:12
要利用 yield 实现一个好用的协程,首先需要理解协程的基本概念:它是能够挂起(暂停)和恢复执行的函数,可以在不同的地方进行任务切换。这种特性使得协程非常适合 I/O 密集型任务和并发任务的调度。
在 Python 中,生成器(通过 yield)是实现协程的一种方式。要实现一个好用的协程,关键在于以下几个方面:
- 暂停和恢复执行:协程能够在执行过程中挂起,等待其他任务的完成,再继续执行。
yield和send()是这类协作的关键。 - 双向通信:协程不仅能够返回数据,还能接收外部数据。
- 任务调度:利用协程,可以调度多个任务进行并发执行。Python 的
asyncio库实际上是基于协程的实现,但在不使用asyncio的情况下,您也可以利用yield来手动调度任务。
实现一个基本的协程
下面是一个基于 yield 实现的简单协程示例,它模拟了一个并发任务调度器。
示例:实现一个简单的协程调度器
这个例子中,我们将实现一个调度器,它会调度多个任务并逐一执行,每个任务会在某个点暂停(通过 yield),然后由调度器恢复。
import time
# 协程任务1
def task1():
print("Task 1 started")
time.sleep(1) # 模拟I/O操作
yield # 挂起任务
print("Task 1 resumed")
# 协程任务2
def task2():
print("Task 2 started")
time.sleep(2) # 模拟I/O操作
yield # 挂起任务
print("Task 2 resumed")
# 调度器:负责调度任务并让它们交替执行
def scheduler(*tasks):
while tasks:
task = tasks.pop(0) # 获取一个任务
try:
next(task) # 启动任务,执行到第一个 yield
tasks.append(task) # 任务没有完成,放回队列继续执行
except StopIteration:
# 任务完成,移出队列
pass
if __name__ == '__main__':
# 初始化任务
t1 = task1()
t2 = task2()
# 启动调度器
scheduler(t1, t2)
解释
- task1() 和 task2(): 这两个函数是协程(生成器),它们在执行过程中会通过 yield 暂停执行,模拟了 I/O 操作。
- scheduler(): scheduler 是一个简单的调度器,它接收多个协程任务,并轮流执行它们。 调度器通过调用 next(task) 启动任务,每次执行到 yield 时,任务会暂停,调度器将任务重新放回队列等待下一次执行。
- 任务切换: yield 会使得协程挂起,而调度器的任务切换机制会使得多个任务能交替进行,模拟了并发执行的效果。
- StopIteration: 当一个协程任务完成时(没有更多的 yield 语句),会抛出 StopIteration 异常,调度器将捕获此异常并移除任务。
输出:
Task 1 started
Task 2 started
Task 1 resumed
Task 2 resumed
改进:增加返回值和任务管理
为了让协程更加实用,您可以加入返回值,任务管理(比如任务状态),以及通过外部输入控制协程的执行。以下是一个更复杂的版本,它允许协程返回结果,并通过外部 send() 方法传递输入:
import time
# 协程任务1
def task1():
print("Task 1 started")
yield 1 # 任务挂起,返回1
print("Task 1 resumed")
return "Task 1 done"
# 协程任务2
def task2():
print("Task 2 started")
yield 2 # 任务挂起,返回2
print("Task 2 resumed")
return "Task 2 done"
# 调度器:负责调度任务并让它们交替执行
def scheduler(*tasks):
while tasks:
task = tasks.pop(0) # 获取一个任务
try:
result = next(task) # 启动任务,执行到第一个 yield
print(f"Task yielded: {result}")
tasks.append(task) # 任务没有完成,放回队列继续执行
except StopIteration as e:
# 任务完成,移出队列,并返回任务的结果
print(f"Task completed with result: {e.value}")
if __name__ == '__main__':
# 初始化任务
t1 = task1()
t2 = task2()
# 启动调度器
scheduler(t1, t2)
解释改进点
yield 返回值:StopIteration 异常处理:- 任务管理:
输出:
Task 1 started
Task 2 started
Task yielded: 1
Task yielded: 2
Task 1 resumed
Task completed with result: Task 1 done
Task 2 resumed
Task completed with result: Task 2 done
总结
通过 yield 实现的协程具有以下优势:
- 暂停和恢复:可以在执行过程中暂停,保存执行状态,等待某些事件(如 I/O 操作)完成后再恢复执行。
- 双向通信:可以通过
send()发送数据到协程中,形成交互式的编程模式。 - 任务调度:可以手动管理任务的执行,控制任务切换,模拟并发执行。
这种基于 yield 的协程实现方法非常适用于 I/O 密集型的任务(如网络请求、文件操作等),并且能高效地调度多个任务,避免了传统线程/进程带来的高开销。
评论(0)