生成器与协程

操作系统会为每个函数分配一个栈帧,但是对于python中生成器所在函数,其栈帧是分配在堆上面的,所以其函数运行状态能够一值保存。此即生成器实现原理。

做个实验,打印生成器函数地址和普通函数地址

def yieldFunc():
    for i in range(5):
        yield i

def normalFunc1():
    for i in range(4, 5):
        return i

def normalFunc2():
    for i in range(3):
        return i

a = yieldFunc()
b = yieldFunc()
c = yieldFunc()
d = normalFunc1()
e = normalFunc2()
print(f"a in yieldFunc: {hex(id(a))}")
print(f"b in yieldFunc: {hex(id(b))}")
print(f"c in yieldFunc: {hex(id(c))}")
print(f"d in normalFunc: {hex(id(d))}")
print(f"e in normalFunc: {hex(id(e))}")

Output

a in yieldFunc: 0x159f65b5b10
b in yieldFunc: 0x159f65b5c78
c in yieldFunc: 0x159f65b5cf0
d in normalFunc: 0x7ffd891f62f0
e in normalFunc: 0x7ffd891f6270

上述实验可以论证,在yield函数中的变量是存在堆中的,普通函数的变量是存在栈中的(我的系统中堆向上增长,栈向下增长)

协程是一种基于生成器的抽象模式。协程不是并行的,协程是单线程的!
协程是一种生产者消费者模型的设计模式。

请看下面代码:

class Producer:
    def produce(self):
        while True:
            data = generateData()
            sendToConsumer(data)

class Consumer:
    def consume(self):
        while True:
            data = receiveFromProducer()
            onReceivedData(data)

Producer一直生产数据,然后发送给Consumer。Consumer一直读取Producer生产的数据,然后做相应处理。这是两个并行的过程,但是他们实际又是相互依赖的,Consumer每次只能处理一个Producer生产的数据。当Consumer处理数据时,Producer被generateData() 或者sendToConsumer() block,当Producer生产数据时,Consumer又会一直被receiveFromProducer() block。特别是有IO操作时对系统的性能影响很大。
所以更好的设计模式应是由Consumer来驱动。参见如下代码:

def sendToConsumer(data):
    originalSendToConsumer(data)
    yield data

class Producer:
    def produce(self):
        while True:
            data = generateData()
            sendToConsumer()

def receiveFromProducer(producer):
    producer.produce()
    data = originalReceiveFromProducer()
    yield data

class Consumer:
    def __init__(self, producer):
        self.producer = producer

    def consume(self):
        while True:
            data = receiveFromProducer(self.producer)
            onReceivedData(data)

producer = Producer()
consumer = Consumer(producer)
while True:
    consumer.consume()

只有当Consumer需要数据时,Producer才生产数据。
更加典型的例子就是管道: produce() | consume()。当consume()需要数据写入时,管道resume(produce())。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值