1

我已经阅读了有关该主题的所有文档,但似乎我无法很好地掌握 Python 协程的整个概念来实现我想做的事情。

我有一个后台任务(它会生成一些随机文件,但这并不重要),它在无限循环中执行此操作(这是一个观察者)。

我想以最有效的方式实现这个后台任务,我认为微线程(又名协程)是实现这一目标的好方法,但我根本无法让它工作(无论是后台任务运行或程序的其余部分,但不能同时进行!)。

有人可以给我一个使用协程实现的后台任务的简单示例吗?还是我认为协程可以用于此目的是错误的?

我正在使用 Python 2.7 原生协程。

我精通并发,尤其是 DBMSes 和 Ada,所以我对底层原理了解很多,但我不习惯生成器即协程的概念,这对我来说很新。

/编辑:这是我的代码示例,我必须再次强调它不起作用:

@coroutine
def someroutine():
    with open('test.txt', 'a') as f:
        f.write('A')
    while True:
        pass
    yield 0

@coroutine
def spawnCoroutine():
    result = yield someroutine()

    yield result

routine = spawnCoroutine()
print 'I am working in parallel!'

# Save 'A' in the file test.txt, but does not output 'I am working in parallel!'

注意:@coroutine 是来自coroutine.py的装饰器,由 David Beazley 提供

/最终编辑和解决方案回顾

好的,我的问题被关闭了,因为它看起来模棱两可,事实上,这正是我的问题的目的:澄清协程在线程和多处理上的使用。

幸运的是,在可怕的制裁发生之前提交了一个很好的答案!

强调上述问题的答案:不,Python 的协程(也不是 bluelet/greenlet)不能用于运行独立的、可能无限受 CPU 限制的任务,因为协程没有并行性

这是最让我困惑的。事实上,并行性是并发的一个子集,因此当前 Python 中的协程实现允许并发任务,但不允许并行任务,这相当令人困惑!这种行为要与 Ada 等并发编程语言的 Tasks 概念明确区分开来。

此外,Python 的线程与协程类似,它们通常在等待 I/O 时切换上下文,因此也不是独立 CPU 密集型任务的理想选择(请参阅 David Beazley 的理解 GIL)。

我目前使用的解决方案是使用multiprocessing模块生成子进程。产生后台进程很繁重,但总比什么都不运行要好。这还具有允许分布式计算的优点。

作为替代方案,在 Google App Engine 上,有deferred 模块background_thread 模块可以为多处理提供有趣的替代方案(例如,通过使用一些实现 Google App Engine API 的库,例如typhoonae,虽然我不确定他们还没有实现这些模块)。

4

2 回答 2

3

如果您查看coroutine.py您正在使用的(普通)库,它包含一个示例,该示例显示了grep“在后台”如何工作。您的代码和示例之间有两个区别:

  1. grep在工作时重复yields — 事实上,它yield是每行一次。你必须这样做,否则除了你的协程之外没有人有机会运行直到它完成。

  2. 主代码重复调用sendgrep程,每行再次调用一次。你必须这样做,否则你的协程永远不会被调用。

这是一个尽可能简单的案例——一个协程,以及一个无条件驱动该协程的简单调度程序。

以下是如何将您的示例转换为有效的方法:

@coroutine
def someroutine():
    with open('test.txt', 'a') as f:
        yield
        f.write('A')
    while True:
        yield
    yield 0

routine = someroutine()
print 'I am working in parallel!'
routine.send()
print 'But only cooperatively...'
routine.send()

等等。

但通常你不想这样做。在grep示例的情况下,协程和主驱动程序作为消费者和生产者明确合作,因此直接耦合非常有意义。您只需要独立安排一些完全独立的任务。

为此,不要尝试自己构建线程。如果您想要协作线程,请使用现成的调度程序/调度程序,您必须对所有任务进行的唯一更改就是yield频繁地调用以有效地共享时间。

如果您甚至不关心线程是否合作,只需使用threadingor multiprocessing,您甚至不需要yields:

def someroutine():
    with open('test.txt', 'a') as f:
        f.write('A')
    while True:
        pass
    return 0

routine = threading.Thread(someroutine)
print 'I am working in parallel!'

PS,正如我在其中一条评论中所说,如果您还没有通过http://www.dabeaz.com/coroutines/index.html或类似的方式工作,那么您真的应该这样做,并在您有任何问题时回来一路去寻找,而不是写你不理解的代码并问为什么它不起作用。我敢打赌,如果你进入第 4 部分(可能更早),你就会明白为什么你最初的问题很愚蠢。

于 2012-11-14T22:18:19.623 回答
1

而真:通过

无尽的循环。

所以在那之后它不会执行yeld。事实上它真正的功能结束,之后的一切都是纯无用的装饰。

而且由于 someroutine 在它可以发出之前就被卡住了(双关语;)),yeld someroutine() 也不会发出声音。

所以你让你的脚本忙着什么都不做。(无限空循环)。

于 2012-11-14T20:43:41.687 回答