python asyncio.Event.wait() 没有响应 event.set()

Posted

技术标签:

【中文标题】python asyncio.Event.wait() 没有响应 event.set()【英文标题】:python asyncio.Event.wait() not responding to event.set() 【发布时间】:2018-02-16 23:27:56 【问题描述】:

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

"""\
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() 调用和set() 调用的事件是相同的。但是在await event.wait() 调用之后,消息“工人有事件”永远不会出现。

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

为什么wait() 永远不会返回?我怎么知道?

【问题讨论】:

如果 add_event_callback 设置的回调是从不同的线程调用的(我怀疑是这种情况,因为它们是在没有任何东西提交到循环的情况下被调用的),那么你不能使用Event.set,这不是线程安全的。相反,请使用loop.call_later_threadsafe(self.set) 看准了!调用 self._loop.call_soon_threadsafe(self.set) 就可以了。谢谢。我只需要添加一个本地 self.guard 以防止在有效负载完成之前重复多个按钮操作:self.is_set() 有同样的问题:-) 【参考方案1】:

add_event_callback 设置的回调是从不同的线程调用的,正如它们被“在后台”自动调用所表明的那样。这意味着您不能直接从 gpio 回调对 asyncio.Event 调用 set,因为 asyncio 类不是线程安全的。

要从不同的线程唤醒asyncio.Event,您可以将event.set 传递给loop.call_soon_threadsafe。在你的情况下,你会改变:

self.set()

到:

self._loop.call_soon_threadsafe(self.set)

【讨论】:

嗯,我找不到 call_later_threadsafe。这不应该是 call_soon_threadsafe 吗? self._loop.call_soon_threadsafe(self.set) 太棒了

以上是关于python asyncio.Event.wait() 没有响应 event.set()的主要内容,如果未能解决你的问题,请参考以下文章

001--python全栈--基础知识--python安装

Python代写,Python作业代写,代写Python,代做Python

Python开发

Python,python,python

Python 介绍

Python学习之认识python