Search code examples
pythongeneratorproducer-consumercoroutine

Understand the producer and receiver using coroutine in python


I want to use coroutine to implement producer and receiver. My idea is using two coroutines , one for producer and one for recevier. But my understand for coroutine's send and running mode is wrong. Here is my code :

def coroutine(func):
    def start(*args,**kwargs):
        cr = func(*args,**kwargs)
        cr.next()
        return cr
    return start

class Producer(object):
    def __init__(self, recevier):
        self.count=1
        self.producer_coroutine = self._producer()
        self.receiver = receiver

    @coroutine
    def _producer(self):
        print "Waiting"
        yield

        while True:
            self.send("Yeah, but no, but yeah, but no")
            get_feedback =  (yield )
            print ("get feedback %s"%get_feedback)
            self.send("A series of tubes")
            break

    def send(self,arg):
        self.receiver.receive_coroutine.send(arg)

    def init_producer(self):
         self.producer_coroutine.send("begin the send and receive")

class Recevier(object):
    def __init__(self):
        self.count=1
        self.receive_coroutine=self._rececive()
        self.producer = None

    def setting_producer(self, producer):
        self.producer = producer

    @coroutine
    def _rececive(self):
        while True:
            line = (yield)
            print("Get line is : %s" %line)
            self.feedback("Got it")

    def feedback(self, arg):
        self.producer.producer_coroutine.send(arg)


receiver = Recevier()
producer = Producer(receiver)
receiver.setting_producer(producer)
producer.init_producer()

Python give me the error:

Waiting
Get line is : Yeah, but no, but yeah, but no
Traceback (most recent call last):
  File "test/test_coroutine.py", line 56, in <module>
    producer.init_producer()
  File "test/test_coroutine.py", line 31, in init_producer
    self.producer_coroutine.send("begin the send and receive")
  File "test/test_coroutine.py", line 21, in _producer
    self.send("Yeah, but no, but yeah, but no")
  File "test/test_coroutine.py", line 28, in send
    self.receiver.receive_coroutine.send(arg)
  File "test/test_coroutine.py", line 47, in _rececive
    self.feedback("Got it")
  File "test/test_coroutine.py", line 50, in feedback
    self.producer.producer_coroutine.send(arg)
ValueError: generator already executing

Update: I find greenlet may implement the communication. Which like this :

from greenlet import greenlet

def test1():
    global a
    print 12, "begin switch"
    gr2.switch()
    a = 4
    print " begin  switch too"
    gr2.switch()

def test2():

    global a
    print 56,"come from test1, to swich"
    gr1.switch()
    print a
    print 78

a=5
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()

Solution

  • When you call producer.init_producer(), the following happens:

    • _producer sends "Yeah, but no, but yeah, but no".
    • _rececive receives "Yeah, but no, but yeah, but no" and prints it.
    • _rececive calls self.feedback.
    • self.feedback sends "Got it" to _producer
    • However, _producer is still at the line self.send("Yeah, but no, but yeah, but no"), because _rececive has not yielded yet.
    • As _producer is not on a yield statement, it cannot receive a send, and so an exception is thrown.

    Therefore, the problem is your feedback loop. I'm not sure why the feedback loop is needed, as if you comment out the send_feedback and get_feedback lines, the program works fine, producing:

    Waiting
    Get line is : Yeah, but no, but yeah, but no
    Get line is : A series of tubes
    Traceback (most recent call last):
      File "d.py", line 58, in <module>
        producer.init_producer()
      File "d.py", line 32, in init_producer
        self.producer_coroutine.send("begin the send and receive")
    StopIteration