asyncio:等待来自其他线程

时间:2015-10-07 18:51:40

标签: python events python-multithreading python-asyncio

我在Python中设计应用程序,它应该访问一台机器来执行一些(冗长的)任务。对于与网络相关的所有内容,asyncio模块似乎是一个不错的选择,但现在我需要访问一个特定组件的串行端口。我已经为实际的串口实现了一种抽象层,但是无法弄清楚如何将它与asyncio明智地集成在一起。

以下设置:我有一个运行循环的线程,它定期与机器对话并解码响应。使用方法enqueue_query(),我可以将一个查询字符串放入一个队列,然后由另一个线程将其发送到机器并引发响应。通过传递threading.Event(或任何具有set()方法的东西),调用者可以执行阻塞等待响应。这可能看起来像这样:

f = threading.Event()
ch.enqueue_query('2 getnlimit', f)
f.wait()
print(ch.get_query_responses())

我的目标是将这些行放入协程并让asyncio处理这个等待,以便应用程序可以在此期间执行其他操作。我怎么能这样做?通过将f.wait()包装到Executor中可能会有效,但这似乎有点愚蠢,因为这会创建一个新线程,只是等待另一个线程做某事。

谢谢! 最好的祝福, 菲利普

2 个答案:

答案 0 :(得分:8)

  

通过传递threading.Event(或任何带有set()方法的东西),调用者可以执行阻塞等待响应。

鉴于查询函数的上述行为,您只需要asyncio.Event的线程安全版本。这只是3行代码:

import asyncio
class Event_ts(asyncio.Event):
    #TODO: clear() method
    def set(self):
        #FIXME: The _loop attribute is not documented as public api!
        self._loop.call_soon_threadsafe(super().set)

功能测试:

def threaded(event):
    import time
    while True:
        event.set()
        time.sleep(1)

async def main():
    import threading
    e = Event_ts()
    threading.Thread(target=threaded, args=(e,)).start()
    while True:
        await e.wait()
        e.clear()
        print('whatever')

asyncio.ensure_future(main())
asyncio.get_event_loop().run_forever()

答案 1 :(得分:2)

最简单的方法是完全按照你的建议 - 在执行者中将f.wait()的调用包裹起来:

@asyncio.coroutine
def do_enqueue():
    f = threading.Event()
    ch.enqueue_query('2 getnlimit', f)
    yield from loop.run_in_executor(None, f.wait)
    print(ch.get_query_responses())

您确实会产生启动线程池的开销(至少对于第一次调用,池将从那时开始保留在内存中),但是任何提供threading.Event()之类的实现的解决方案线程安全的阻塞和非阻塞API,不依赖于内部的任何后台线程,将会有更多的工作。