Python 如何将第三方库中的函数转换为异步函数?

Python 如何将第三方库中的函数转换为异步函数?,python,asynchronous,python-asyncio,Python,Asynchronous,Python Asyncio,我正在使用我的Raspberry Pi和pigpio和websockets库 我希望程序异步运行(即,我将使用async def main作为入口点) pigpio库希望调用一个同步函数来响应事件,这很好,但是我想从该回调中调用websocket库中的另一个异步函数 所以它看起来像: def sync_cb(): # <- This can not be made async, therefore I can not use await [ws.send('test') for ws

我正在使用我的Raspberry Pi和
pigpio
websockets

我希望程序异步运行(即,我将使用
async def main
作为入口点)

pigpio
库希望调用一个同步函数来响应事件,这很好,但是我想从该回调中调用
websocket
库中的另一个异步函数

所以它看起来像:

def sync_cb(): # <- This can not be made async, therefore I can not use await
   [ws.send('test') for ws in connected_ws] # <- This is async and has to be awaited
但是不鼓励使用
asyncio.run

因此,我的同步回调需要调用与同步函数异步的
ws.send
(也来自)

另一个可行的选择是:

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(asyncio.gather(*[ws.send(json.dumps(message)) for ws in connected_ws]))
但是仅仅为了运行一个简单的异步函数,创建和设置一个偶数循环的三行代码听起来就太多了

我的问题是:

  • 是否可以在需要同步回调的情况下替换异步函数(即,在本例中是否有方法使
    cb
    async)
  • 而且,使用
    asyncio.run
    asyncio.wait
    调用一个简单的异步方法(在列表中)会产生什么样的开销
在需要同步回调的情况下,是否可以替换异步函数

这是可能的。您可以在单独的线程中运行事件循环,并在那里发出“代码> AycNc<代码>代码,但是您必须考虑吉尔。

导入异步IO
导入线程
班级门户:
定义初始化(自停止事件):
self.loop=asyncio.get\u event\u loop()
self.stop\u事件=stop\u事件
异步定义调用(self、fn、args、kwargs):
返回等待fn(*args,**kwargs)
异步定义停止(自):
self.stop_event.set()
def呼叫(自我、fn、*args、**kwargs):
返回asyncio.run\u coroutine\u threadsafe(self.\u调用(fn、args、kwargs)、self.loop)
def停止(自):
返回self.call(self.\u stop)
def create_portal():
入口=无
异步def wait_stop():
非本地门户
stop\u event=asyncio.event()
入口=入口(停止事件)
正在运行_event.set()
等待停止事件。等待()
def run():
asyncio.run(wait\u stop())
正在运行\u event=threading.event()
线程=线程。线程(目标=运行)
thread.start()
正在运行\u事件。等待()
返回入口
用法示例:

async def测试(msg):
等待异步睡眠(0.5)
打印(msg)
返回“HELLO”+msg
#它将在单独的线程中运行一个新的事件循环
门户=创建门户()
#它将在单独的线程中调用'test',并返回Future
打印(portal.call(test,“WORLD”).result())
portal.stop().result()
就你而言:

def sync_cb():
    calls = [portal.call(ws.send, 'test') for ws in connected_ws]
    # if you want to get results from these calls:
    # [c.result() for c in calls]
而且,使用asyncio.run和asyncio.wait调用一个简单的异步方法会产生什么样的开销

asyncio.run
将创建一个新的事件循环,然后将其关闭。如果不经常调用回调,很可能不会有问题。但是,如果您将在另一个回调中使用
asyncio.run
,那么它们将不能同时工作。

您可以使用返回
concurrent.furres.Future的函数(可以同步等待)将协程包装到常规函数,并从同步代码调用它

据我所知,如果(第三方库的)同步代码在单独的线程中执行,那么这种方法更合适,但是经过一些修改,它可以适应单线程执行

举例说明该方法:

导入异步IO
def异步到同步(循环,foo):
def foo_uuz(*args,**kwargs):
返回asyncio.run\u coroutine\u threadsafe(foo(*args,**kwargs),loop).result()
返回foo_
def同步_代码(cb):
对于范围(10)内的i:
cb(一)
异步定义异步_cb(a):
打印(“异步回调:”,a)
异步def main():
loop=asyncio.get\u event\u loop()
等待循环。在执行器中运行(无,同步代码,异步到同步(循环,异步cb))
asyncio.run(main())
输出:

async callback: 0
async callback: 1
async callback: 2
...

您似乎在
foo\uu
中缺少一个
返回值better@AlexNoname我很欣赏您的示例,但是如果不使用
run\u in\u executor
,只运行函数似乎更容易。我更新了我的示例。理想情况下,我可以跳过所有事件循环的创建。最好提供一个最小的可复制示例,因为现在我不清楚您希望如何“完全在异步IO事件循环中运行”,但同时“跳过所有事件循环的创建”如果库是用asyncio编写的,并且需要非异步回调,则会出现问题。我很惊讶,在另一个事件循环中运行时,使用
asyncio.run()
不会引发异常。此外,如果您需要在回调中调用本质上是阻塞的代码,您可以无限期地阻塞外部事件循环。还不清楚什么是
连接的\u ws
-如果这些是在某些事件循环下创建的活动websocket连接,我认为您不能直接将它们交给其他事件循环(
asyncio.run
每次都创建一个新的事件循环,很像问题第二部分中的三行)。即使它现在正好工作,也可能会在没有通知的情况下停止工作-使用asyncio不受支持。有两个库。一个库需要非异步回调,另一个库需要异步回调。我想从非异步函数中调用一个异步库函数。令人困惑的是,您开始了这个问题“我试图使用这个库编写一个完全在asyncio事件循环中运行的程序”,然后下一句说“库需要一个[sync]回调函数”。这两个段落是否实际引用了两个不同的库?在您的位置,我将创建一个带有事件循环的专用线程,该循环只运行
循环。永远运行()
,并向该循环提交协程
async callback: 0
async callback: 1
async callback: 2
...