python asyncio.Event.wait() not responding to event.set()
计划是使多个IO例程"同时"运行(特别是在Raspberry Pi上,操作IO引脚并同时运行SPI接口)。我尝试使用asyncio来实现这一目标。但是,我的简单试用无法运行。
这是该代码的简化版本,省略了IO引脚的详细信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | """\\ Reduced problem representation: this won't run because GPIO details have been left out """ import RPi.GPIO as gpio import asyncio GPIO_PB = 12 # Define pushbutton channel async def payload(): """ Provides some payload sequence using asyncio.sleep()""" #Payload action await asyncio.sleep(1) #Payload action await asyncio.sleep(1) class IOEvent(asyncio.locks.Event): """\\ Create an Event for asyncio, fired by a callback from GPIO The callback must take a single parameter: a gpio channel number """ def __init__(self, ioChannel, loop): super().__init__(loop = loop) self.io = ioChannel def get_callback(self): "The callback is a closure that knows self when called" def callback( ch ): print("callback for channel {}".format(ch)) if ch == self.io and not self.is_set(): print(repr(self)) self.set() print(repr(self)) return callback async def Worker(loop, event): print("Entering Worker: {}".format(repr(loop))) while loop.is_running(): print("Worker waiting for {}".format(repr(event))) await event.wait() print("Worker has event") event.clear() await payload() print("payload ended") loop = asyncio.get_event_loop() # Create an event for the button pb_event = IOEvent( GPIO_PB, loop) # register the pushbutton's callback # Pushing the button calls this callback function gpio.add_event_callback( GPIO_PB, pb_event.get_callback() ) try: asyncio.ensure_future(Worker(loop, pb_event)) loop.run_forever() except KeyboardInterrupt: pass finally: print("Closing Loop") loop.stop() loop.close() |
我得到的输出是这样的:
1 2 3 4 5 6 | Entering Worker: <_UnixSelectorEventLoop running=True closed=False debug=False> Worker waiting for <__main__.IOEvent object at 0x76a2a950 [unset]> callback for channel 12 <__main__.IOEvent object at 0x76a2a950 [unset,waiters:1]> <__main__.IOEvent object at 0x76a2a950 [set,waiters:1]> callback for channel 12 |
这些行重复显示按钮并正确触发其回调例程。第一次按预期调用
我查看了PyQt5和asyncio:从不完成收益,但是除了默认循环,我看不到其他任何循环。
为什么
由
回调必须从不同的线程调用,因为它们是自动调用的。这意味着您不能在
要从其他线程唤醒
1 | self.set() |
至:
1 | self._loop.call_soon_threadsafe(self.set) |