Python 2.7中如何在同一事件循环中遍历两个异步生成器
如何在Python 2.7中同时消费两个生成器(或ZeroMQ订阅者)
这个问题我之前也碰到过,你原来的写法确实只会一直输出1——因为生成器对象在布尔判断里属于真值,gen_one() or gen_two()会直接返回第一个生成器,循环自然只会迭代它,完全碰不到第二个生成器。
要实现按各自的产出顺序消费,得用多路复用或者线程+队列的思路,下面分场景给你具体方案:
方案一:针对实际ZeroMQ订阅者场景(推荐)
ZeroMQ的套接字支持用select模块做IO多路复用,这是最适合的方案,不需要额外线程,效率也高。我们可以把两个订阅者套接字注册到可读监听列表里,哪个有数据就处理哪个:
import zmq import select context = zmq.Context() # 初始化第一个订阅者 sub1 = context.socket(zmq.SUB) sub1.connect("tcp://localhost:5555") sub1.setsockopt(zmq.SUBSCRIBE, "") # 订阅所有消息 # 初始化第二个订阅者 sub2 = context.socket(zmq.SUB) sub2.connect("tcp://localhost:5556") sub2.setsockopt(zmq.SUBSCRIBE, "") # 要监听的可读套接字集合 read_sockets = [sub1, sub2] while True: # 阻塞等待至少一个套接字可读 readable, _, _ = select.select(read_sockets, [], []) for sock in readable: # 读取并处理消息 data = sock.recv() print(data)
这样不管哪个订阅者先收到消息,都会被及时处理,完全符合你期望的“谁先就绪谁先输出”的逻辑。
方案二:针对生成器模拟场景
如果只是用生成器模拟这个逻辑,因为生成器的yield是同步阻塞的,我们可以用线程把每个生成器的产出放到线程安全的队列里,主线程从队列取数据即可:
import time import threading from Queue import Queue def gen_one(q): while True: q.put(1) time.sleep(1) def gen_two(q): while True: q.put(2) time.sleep(1.3) # 创建线程安全的队列 msg_queue = Queue() # 启动两个线程分别运行生成器 t1 = threading.Thread(target=gen_one, args=(msg_queue,)) t2 = threading.Thread(target=gen_two, args=(msg_queue,)) # 设置为守护线程,主线程退出时自动结束 t1.daemon = True t2.daemon = True t1.start() t2.start() # 主线程持续从队列取数据输出 while True: item = msg_queue.get() print(item)
运行这段代码,输出就会和你期望的一致:1、2、1、2、1、2、1、1、2...
补充:为什么原代码失效?
再啰嗦一句,你原代码里的for a in gen_one() or gen_two(),gen_one()返回的是一个非空的生成器对象,在Python的布尔判断中会被视为True,所以or运算符直接返回第一个生成器,循环只会迭代它,自然一直输出1,完全不会触发第二个生成器的执行。
内容的提问来源于stack exchange,提问作者sagism
相关产品推荐
相关产品推荐

