简体   繁体   English

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

[英]How to create non-blocking looping coroutines using python generators?

I am using python and I'm experimenting with using generators as coroutines. 我正在使用python,我正在尝试使用生成器作为协同程序。 Meaning that I'm using the yield expression to pass values into the generator and then sending messages back and forth between various of these generator coroutines. 这意味着我正在使用yield表达式将值传递给生成器,然后在各种生成器协同程序之间来回发送消息。

I am trying to chain together coroutines into a loop that iterates a value, whilst remaining open to new values originating from outside the loop. 我试图将协程链接到一个迭代一个值的循环,同时保持对源自循环外部的新值的开放。 In other words, the loop should be non-blocking: 换句话说,循环应该是非阻塞的:

This is the loop: 这是循环:

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)

And from time to time I want to pass a new value from the outside of this loop to coroutine_A, and then off it goes again. 并且有时我想从这个循环的外部传递一个新值到coroutine_A,然后再次关闭它。

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

The individual pieces work fine, but two issues arise when I try to connect them up. 单个部分工作正常,但当我尝试连接它们时会出现两个问题。 Firstly, how to instantiate these as a loop, which seems doable, but leads to a deeper issue as elaborated on beneath. 首先,如何将这些实例化为循环,这似乎是可行的,但会导致更深层次的问题,如下所述。

THE FIRST ISSUE: 第一个问题:

When instantiating coroutine_A, coroutine_B doesn't exist yet, so it isn't yet possible to tell coroutine_A what its message target is. 在实例化coroutine_A时,coroutine_B尚不存在,因此尚无法告诉coroutine_A它的消息目标是什么。 Basically a chicken and egg scenario. 基本上是鸡肉和鸡蛋的场景。

I've tried creating a container function that instances each of these coroutines (without message targets), and then creates a loop that manages the message on behalf of the coroutines like so: 我已经尝试创建一个容器函数来实例化每个协同程序(没有消息目标),然后创建一个循环来代表协同程序管理消息,如下所示:

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)

The problem with this is that it doesn't then seem possible to pass messages in from outside the loop because the while loop just gets stuck doing its thing. 这样做的问题是,它似乎不可能从循环外传递消息,因为while循环只是卡在做它的东西。

Another way around this is to instantiate coroutine_A with a nested yield expression so that the target can be passed-in after instantiation time: 另一种方法是使用嵌套的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

However, when coroutine_C tries to send a message to coroutine_A I get a ValueError exception: "generator already executing". 但是,当coroutine_C尝试向coroutine_A发送消息时,我得到一个ValueError异常:“生成器已经执行”。

So both of these strategies basically lead to: 因此,这两种策略基本上都会导致:

THE DEEPER ISSUE: 更深层次的问题:

It appears that generators as coroutines can't loop back on themselves, and it seems that the reason for this is that the send call is a 'normal method', so effectively trying to chain a call-stack back on itself, ie doesn't allow recursion / reentry per David Beazley's Generators: The Final Frontier pages 127 to 131. 似乎作为协同程序的生成器无法自行循环,似乎这样做的原因是发送调用是一种“正常方法”,因此有效地尝试将调用堆栈链接回自身,即没有'允许每个David Beazley的Generators:The Final Frontier第127到131页的递归/重新进入。

So its necessary to hand-off the information to a queue system and then dequeue and start a new call. 因此,有必要将信息传递给队列系统,然后出列并开始新的呼叫。 But when I try this, I seem to get stuck with While loops that block messages originating from outside of the loop. 但是当我尝试这个时,我似乎陷入了阻止源自循环外部的消息的While循环。

So, long story short, how does one, on one hand, keep the loop cranking over on itself while, on the other hand, remaining open to new messages originating from outside the loop? 所以,长话短说,一方面,一方面如何保持循环自身,而另一方面,如何保持对来自循环外部的新消息保持开放状态?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM