【问题标题】:How to create non-blocking looping coroutines using python generators?如何使用 python 生成器创建非阻塞循环协程?
【发布时间】:2015-05-23 18:47:16
【问题描述】:

我正在使用 python,并且正在尝试使用生成器作为协程。这意味着我使用 yield 表达式将值传递给生成器,然后在这些生成器协程之间来回发送消息。

我正在尝试将协同程序链接到一个循环中,该循环迭代一个值,同时对源自循环外部的新值保持开放。换句话说,循环应该是非阻塞的:

这是循环:

coroutine_A -> val = (yield) -> does something to val -> coroutine_B.send(other_val)
coroutine_B -> val = (yield) -> does something to val -> coroutine_C.send(other_val)
coroutine_C -> val = (yield) -> does something to val -> coroutine_A.send(other_val)

有时我想将这个循环外部的一个新值传递给 coroutine_A,然后再关闭它。

EXTERNAL TO LOOP -> coroutine_A.send(message) -> loop continues from new value...

各个部分都可以正常工作,但是当我尝试将它们连接起来时会出现两个问题。首先,如何将这些实例化为一个循环,这似乎可行,但会导致下面详述的更深层次的问题。

第一期:

在实例化 coroutine_A 时,coroutine_B 还不存在,所以还不能告诉 coroutine_A 它的消息目标是什么。基本上是先有鸡还是先有蛋的场景。

我尝试创建一个容器函数来实例化每个协程(没有消息目标),然后创建一个循环来代表协程管理消息,如下所示:

def func():
    A = coroutine_A()
    next(A)
    B = coroutine_B()
    next(B)
    C = coroutine_C()
    next(C)
    message_A = A.send(None)
    while True:
        message_B = B.send(message_A)
        message_C = C.send(message_B)
        message_A = A.send(message_C)

这样做的问题是,似乎不可能从循环外部传递消息,因为 while 循环只是卡在做它的事情。

解决这个问题的另一种方法是使用嵌套的 yield 表达式实例化 coroutine_A,以便在实例化时间之后可以传入目标:

def coroutine_A():
    while True:
        val = (yield)
        if val is not None:
            coroutine_B_target = val
            while True:
                val = (yield)
                if val is not None:
                    do something to val
                    coroutine_B_target.send(other_val)

A = coroutine_A()
next(A) # prime coroutine
A.send(B) # send in coroutine_B target and step into inner while loop

但是,当 coroutine_C 尝试向 coroutine_A 发送消息时,我得到一个 ValueError 异常:“生成器已在执行”。

所以这两种策略基本上都会导致:

更深层次的问题:

似乎生成器作为协程不能在自身上循环,这似乎是因为发送调用是一种“正常方法”,因此有效地尝试将调用堆栈链接回自身,即不允许根据 David Beazley 的 Generators: The Final Frontier 第 127 至 131 页进行递归/重入。

因此有必要将信息传递给队列系统,然后出列并开始新的呼叫。但是当我尝试这个时,我似乎陷入了阻塞来自循环外部的消息的 While 循环。

那么,长话短说,如何一方面保持循环自行运转,另一方面又对来自循环外部的新消息保持开放?

【问题讨论】:

  • 也许这很接近:jeffknupp.com/blog/2013/04/07/….
  • @HuStmpHrrr 我来看看 Tornado 的内部结构,虽然我的理解是 Tornado 一次只能处理一条消息,即使对该消息的处理可以异步处理?跨度>
  • Tornado 是单线程、非阻塞、异步、事件驱动的(副作用:不需要线程安全问题)。所有代码都是以非阻塞方式编写的。所以是的,一次一条消息。但是,它与 GIL 更相关。由于 cpython 的病态线程模型,几乎不可能用并发工作流实现真正的异步,使其看起来像这样的唯一方法是同时使用线程池(将由 GIL 序列化)和子进程与信号同步(并发但很重)。

标签: python loops generator yield coroutine


【解决方案1】:

【讨论】:

    猜你喜欢
    • 2017-12-15
    • 2013-11-13
    • 1970-01-01
    • 2019-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-27
    • 1970-01-01
    相关资源
    最近更新 更多