在尝试使用select()
(在Python 3.6上)实现类似asyncio
的功能时,我遇到了一些我无法理解的行为:
Queue.put
select()
获取第一个可用的消息(即asyncio.wait_for
类功能。民意调查就像这样:
poll = list(_.get() for _ in queues)
while True:
done, pending = await asyncio.wait(tuple(poll), return_when=asyncio.FIRST_COMPLETED)
然后,迭代done
期货,我用poll
替换Queue.get
中的相应条目:
for f in done:
try:
i = poll.index(f._coro)
...
poll[i] = queues[i].get()
现在,我遇到的奇怪行为是选择有效,但部分done
期货.result()
返回None
,并且通过队列发送的消息将丢失:< / p>
PUT B ('B', 0)/0
GET B ('B', 0)/0
PUT A ('A', 0)/0
GET A None/0 << ('A', 0) is never received
PUT A ('A', 1)/0
GET A ('A', 1)/0
PUT B ('B', 1)/0
GET B None/0
PUT A ('A', 2)/0
GET A None/0
PUT B ('B', 2)/0
GET B None/0
这是代码
#!/usr/bin/env python3
import asyncio, random
async def queue_generator( name, queue, speed=1 ):
counter = 0
while True:
t = (random.random() + 0.5) * speed
await asyncio.sleep(t)
m = (name, counter)
print ("PUT {0} {1}/{2:d}".format(name, m, queue.qsize()))
queue.put_nowait(m)
counter += 1
async def select( *queues ):
poll = list(_.get() for _ in queues)
while True:
# That's the select()-like functionalit
done, pending = await asyncio.wait(tuple(poll), return_when=asyncio.FIRST_COMPLETED)
for f in done:
i = poll.index(f._coro)
# That's where sometimes v is None
v = f.result()
print ("GET {0} {1}/{2}".format("AB"[i], v, queues[i].qsize()))
poll[i] = queues[i].get()
await asyncio.sleep(0.5)
if __name__ == "__main__":
loop = asyncio.get_event_loop()
queue_a = asyncio.Queue(10)
queue_b = asyncio.Queue(10)
tasks = (
loop.create_task(queue_generator("A", queue_a, 3)),
loop.create_task(queue_generator("B", queue_b, 3)),
loop.create_task(select(queue_a, queue_b))
)
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
当发生当前有消息的队列发生变化时会发生这种情况,我认为问题在于对pending
期货做些什么。实际上,添加
for f in pending:
f.cancel()
解决了这个问题,但我想尽可能多地重复利用这些未来。我想这个问题来自于asyncio.wait_for
将生成器列表静默转换为任务这一事实。
答案 0 :(得分:1)
我没有深入研究会发生什么,但移动poll
创建内部循环似乎解决了问题:
async def select( *queues ):
while True:
poll = list(_.get() for _ in queues) # HERE
# That's the select()-like functionalit
done, pending = await asyncio.wait(tuple(poll), return_when=asyncio.FIRST_COMPLETED)
poll
是协程列表。每个独特的协程通常都应该等待一次。否则可能导致奇怪的事情。