【问题标题】:Python loop in a coroutine [closed]协程中的Python循环[关闭]
【发布时间】:2012-11-03 09:20:21
【问题描述】:

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

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

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

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

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

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

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

@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 限制的任务,因为 协程没有并行性。

这是最让我困惑的。确实,parallelism is a subset of concurrency,因此当前 Python 中的协程实现允许并发任务,但不允许并行任务,这相当令人困惑!这种行为要与 Ada 等并发编程语言的 Tasks 概念明确区分开来。

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

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

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

【问题讨论】:

  • 你的代码在哪里? (应该在这里)。
  • 您是在 Python 2.x 生成器、3.x 生成器、无堆栈(或 PyPy)本机协程或其他不同的基础上做协程吗?如果是 2.x,您是否通过 dabeaz.com/coroutines/index.html 工作过?有大量的代码示例,当然整个过程都是为了让您掌握概念。
  • @JohnGainesJr。该术语至少在python-* 邮件列表中很常见,它指的是使用res = yield foo(现在是res = yield from foo)结构进行通信的“生成器”。该术语也可以追溯到引入这些功能的原始 PEP。
  • @user1121352 并发与并行并不完全相同,这似乎是您所要求的。基于yield/next()/send() 的协程本身并不是并行的,除非您将它们与线程或greenlets 混合使用。
  • 对于这个特定的任务,你可能不应该使用协程,而应该使用真正的线程。

标签: python multithreading generator coroutine


【解决方案1】:

当真时: 通过

无限循环。

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

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

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

【讨论】:

  • 是的,这是一个例子,在循环内我可以做其他事情,但目标是这个循环不会停止脚本的其余部分。所以我想这是不可能使用协程的?
  • 不不不!!!!如果你做无限循环“yeld”将不会被执行!!!将 yeld 移入循环。
【解决方案2】:

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

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

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

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

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

@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 以有效地共享时间。

如果您甚至不关心线程是否协作,只需使用threadingmultiprocessing,您甚至不需要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,正如我在其中一个 cmets 中所说,如果您还没有通过 http://www.dabeaz.com/coroutines/index.html 或类似的方法,您真的应该这样做,并在此过程中找到任何问题,而不是编写代码你不明白并问为什么它不起作用。我敢打赌,如果你进入第 4 部分(可能更早),你就会明白为什么你最初的问题很愚蠢。

【讨论】:

  • 完整而准确的答案。事实上,我的任务是完全独立的,而且我认为 Python 协程也被描述为任务(即使在 David Beazley 的演讲中!),与其他并发编程语言(如 Ada)中的任务相同。天哪,我是不是弄错了!实际上它只是并发编程,它在执行 I/O 时切换上下文,有点像当前的 Python 线程在 I/O 上释放 GIL。还有这个post helped me to understand the difference between IO bound tasks and CPU bound tasks
  • 感谢您的解释,我将使用multiprocessing 模块,它更适合我的情况(完全独立的 CPU 绑定任务)。
  • 我想补充的最后一条评论:事实上,Python 的协程和 Bluelets/Greenlets 用于实现没有并行性的并发。实际上一切都是按顺序完成的(我们只是在协程等待 I/O 时切换上下文)。实际上,这简化了事件驱动应用程序(例如 GUI 或 Web 应用程序)的大部分并发代码,因为没有混乱的并行性可以解决。但是当您真正需要并行性时,它会受到限制。发现这个事实对我来说是最有启发性的。
  • @user1121352:大多数人(尤其是那些在 Microsoft-/Java 风格的“多线程一切皆有可能”风格中长大的人)不欣赏这种区别,也不欣赏它为什么有用。 (一旦你遇到问题,你就会明白为什么要混合和匹配——例如,要同时运行 5000 个长期任务,同时有效利用 8 个内核,你可能会在线程或进程池上运行 greenlet……) .
猜你喜欢
  • 1970-01-01
  • 2021-08-14
  • 2018-08-12
  • 1970-01-01
  • 1970-01-01
  • 2020-09-08
  • 1970-01-01
  • 1970-01-01
  • 2017-06-25
相关资源
最近更新 更多