代码之家  ›  专栏  ›  技术社区  ›  LenRem

python异步。事件wait()未响应事件。集合()

  •  5
  • LenRem  · 技术社区  · 6 年前

    计划是让几个IO例程“并发”运行(特别是在Raspberry Pi上,操作IO引脚并同时运行SPI接口)。我尝试使用asyncio来实现这一点。然而,我的简单试用拒绝运行。
    这是代码的简化版本,省略了IO pin详细信息:

    """\
    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()
    

    我得到的输出如下:

    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
    

    这些行显示按钮重复且正确地触发其回调例程。第一次调用 set() 功能如预期。用于 wait() 电话和 集合() 通话内容相同。但是信息 “工作进程有事件” await event.wait() 呼叫从未出现。

    我看着 PyQt5 and asyncio: yield from never finishes ,但除了默认循环之外,我看不到任何其他循环。

    为什么 等待() 永远不回来?我怎么知道?

    1 回复  |  直到 6 年前
        1
  •  5
  •   user4815162342    2 年前

    回调设置者 add_event_callback 从不同的线程调用,如“在后台”自动调用所示。这意味着你不能打电话 set 在上 asyncio.Event 直接从gpio回调,因为异步IO类不是线程安全的。

    唤醒 异步IO。事件 从另一个线程,您可以传递 event.set loop.call_soon_threadsafe . 在您的情况下,您将更改:

    self.set()
    

    收件人:

    self._loop.call_soon_threadsafe(self.set)