我正在阅读gevent教程并看到了这个有趣的片段:
import gevent
def foo():
print('Running in foo')
gevent.sleep(0)
print('Explicit context switch to foo again')
def bar():
print('Explicit context to bar')
gevent.sleep(0)
print('Implicit context switch back to bar')
gevent.joinall([
gevent.spawn(foo),
gevent.spawn(bar),
])
执行流程就像这个foo - >吧 - > foo - >吧。 如果没有gevent模块但是使用yield语句,是不是可以做同样的事情? 我一直试图用'收益'来做这件事,但由于某种原因我不能让它工作...... :(
答案 0 :(得分:5)
用于此目的的生成器通常称为任务(在许多其他术语中),为了清楚起见,我将在此处使用该术语。对的,这是可能的。事实上,有些方法在某些情况下有效并且有意义。但是,没有(我知道)没有gevent.spawn
和gevent.joinall
中的至少一个的等效工作。功能更强大,设计更好的两者需要相同的功能。
根本问题是:发电机可以暂停(当它们点击yield
时),但就是这样。要再次启动它们,您需要在其上调用next()
的其他代码。实际上,您甚至需要在新创建的生成器上调用next()
,以便开始任何。
同样,发电机本身并不是决定下一步应该运行的最佳位置。所以你需要一个循环来启动每个任务的时间片(将它们运行到下一个yield
)并在它们之间无限期地切换。这通常称为调度程序。它们往往很快变得毛茸茸,所以我不会尝试在一个答案中编写完整的调度程序。然而,我可以尝试解释一些核心概念:
yield
视为将控制权交还给sheduler(实际上类似于代码中的gevent.sleep(0)
)。这意味着,生成器会执行任何想要执行的操作,并且当它位于上下文切换方便且可能有用的位置时,它yield
s。yield from
是一个非常有用的工具,可以委托给另一个生成器。如果您不能使用它,则必须使调度程序模拟调用堆栈并将返回值路由到正确的位置,并在任务中执行result = yield subtasks()
之类的操作。这样做更慢,更复杂,并且不太可能产生有用的堆栈跟踪(yield from
免费执行此操作)。但直到最近,这是我们所拥有的最好的。yield
特殊值。yield [from]
可能运行的内容调度程序代码。例如,你可以确定一段代码是否是原子的(wrt其他任务;如果你添加线程到混合,你必须独立地担心它们)只需查看代码,而不检查任何东西它来电。最后,您可能会对Greg Ewing tutorial感兴趣创建这样的调度程序。 (这是在python-ideas
上进行的,同时对现在的PEP 3156进行了头脑风暴。这些邮件线程也可能对您感兴趣,尽管基于Web的存档并不适合在几十个写入的线程中读取数百封邮件半年前。)
答案 1 :(得分:2)
关键是要意识到你必须提供自己的驾驶循环 - 我在下面提供了一个简单的演示。我很懒,并使用Queue对象提供FIFO,我有一段时间没有使用python进行重大项目。
#!/usr/bin/python
import Queue
def foo():
print('Constructing foo')
yield
print('Running in foo')
yield
print('Explicit context switch to foo again')
def bar():
print('Constructing bar')
yield
print('Explicit context to bar')
yield
print('Implicit context switch back to bar')
def trampoline(taskq):
while not taskq.empty():
task = taskq.get()
try:
task.next()
taskq.put(task)
except StopIteration:
pass
tasks = Queue.Queue()
tasks.put(foo())
tasks.put(bar())
trampoline(tasks)
print('Finished')
跑步时:
$ ./coroutines.py
Constructing foo
Constructing bar
Running in foo
Explicit context to bar
Explicit context switch to foo again
Implicit context switch back to bar
Finished