如何使用python生成器创建非阻塞循环协同程序?

时间:2015-03-20 11:12:24

标签: python loops generator yield coroutine

我正在使用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循环。

所以,长话短说,一方面,如何让循环保持循环,另一方面,如何保持对来自循环外部的新消息保持开放状态? < / p>

1 个答案:

答案 0 :(得分:0)

或许,请查看Stackless PythonCircuits Framework