首页 > 解决方案 > 如何为异步生成器提供来自 python.net 的事件

问题描述

我通过 Python for .Net 在 Python 3.6 中使用第三方 .Net 库,它使用 EventHandlers 向我的应用程序提供异步数据,类似于下面的玩具示例:

import clr
from System import Timers

def tock(__, args):
    print(args.SignalTime)

timer = Timers.Timer()
timer.Interval = 1000
timer.Elapsed += tock
timer.Enabled = True
while True:
    pass

我想将这些数据放入异步生成器中,例如:

import clr
from System import Timers

async def tock(__, args):
    yield args.SignalTime

async def main():
    result = await tock
    print(result)

timer = Timers.Timer()
timer.Interval = 1000
timer.Elapsed += tock
timer.Enabled = True
while true:
    result = await timer
    print result

显然只是在事件处理函数上抛出asycand并设置计时器不会这样做,但是有没有一种简单的方法来实现这一点?yieldawait

标签: python.netasynchronouspython-asynciopython.net

解决方案


由于“异步”数据来自另一个线程,因此您需要在 asyncio 和调用tock. 在 asyncio 方面,您将需要实现一个异步生成器并使用循环对其进行迭代async for。例如(未经测试):

import clr, asyncio
from System import Timers

def adapt_sync_to_async():
    # Adapt a series of sync callback invocations to an async
    # iterator. Returns an async iterator and a feed callback
    # such that the async iterator will produce a new item
    # whenever the feed callback is fed one.
    loop = asyncio.get_event_loop()
    queue = asyncio.Queue()
    def feed(item):
        loop.call_soon_threadsafe(queue.put_nowait, item)
    async def drain():
        while True:
            yield await queue.get()
    return drain, feed

tock, feed_tock = adapt_sync_to_async()

async def main():
    async for result in tock():
        print(result)

timer = Timers.Timer()
timer.Interval = 1000
timer.Elapsed += lambda _, args: feed_tock(args.SignalTime)
timer.Enabled = True

asyncio.get_event_loop().run_until_complete(main())

推荐阅读