首页 > 解决方案 > 如何覆盖同步函数以运行异步代码

问题描述

我正在寻找定义一个新的连接,如下所示:https ://udsoncan.readthedocs.io/en/latest/udsoncan/connection.html#defining-a-new-connection

但是,我想从那里调用异步代码(即使发送/等待异步)。我似乎无法让它工作。

考虑以下示例作为我要实现的目标:

import asyncio

async def some_task():
    await asyncio.sleep(1)  # Async task
    print("Done")
    
def sync_method():
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.sleep(5)) # Some async event
    print("sure")

async def main():
    sync_method()
    await asyncio.gather(some_task(), some_task(), some_task())
    

if __name__ == "__main__":
    asyncio.run(main())

但这会引发错误(从未等待睡眠;但如果我等待它,则会收到从非异步函数调用的等待错误)。但我读到(见这里)这是如何从同步函数调用异步函数。

所以,基本上我的代码是异步的,并且main在事件循环中运行。现在,我想调用同步函数,但本质上是想通过使用异步方法使其成为非阻塞的。

我究竟做错了什么?

编辑 - 按要求回溯...

Traceback (most recent call last):
  File ".\tmp_sync_async.py", line 18, in <module>
    asyncio.run(main())
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.1520.0_x64__qbz5n2kfra8p0\lib\asyncio\runners.py", line 43, in run
    return loop.run_until_complete(main)
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.1520.0_x64__qbz5n2kfra8p0\lib\asyncio\base_events.py", line 616, in run_until_complete
    return future.result()
  File ".\tmp_sync_async.py", line 13, in main
    sync_method()
  File ".\tmp_sync_async.py", line 9, in sync_method
    loop.run_until_complete(asyncio.sleep(3))
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.1520.0_x64__qbz5n2kfra8p0\lib\asyncio\base_events.py", line 592, in run_until_complete
    self._check_running()
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.1520.0_x64__qbz5n2kfra8p0\lib\asyncio\base_events.py", line 552, in _check_running
    raise RuntimeError('This event loop is already running')
RuntimeError: This event loop is already running
sys:1: RuntimeWarning: coroutine 'sleep' was never awaited

更新:根据我的发现,run_until_complete如果从同步代码开始然后尝试运行异步代码(即,如果main()在我的情况下是同步代码),则方法很好。

标签: pythonpython-asyncio

解决方案


您可以使用生成器返回协程:

import asyncio


async def some_task():
    await asyncio.sleep(1)  # Async task
    print("Done")

    
def sync_method():
    # A yield statement converts a function into a generator
    # The yield statement effectively freezes the function and no code below a yield is executed until the next item is requested. The earlier asyncio implementations were actually done using yield statements.
    yield asyncio.sleep(5) # Some async event
    print("sure")


async def run_promises(future_generator):
    # Fetch the items from the generator one by one
    for future in future_generator:
        # Wait for the future result, the next yield is not requested until this is done
        await future


async def main():
    # To run in the background:
    asyncio.create_task(run_promises(sync_method()))

    # To wait:
    await run_promises(sync_method())

    await asyncio.gather(some_task(), some_task(), some_task())
    

if __name__ == "__main__":
    asyncio.run(main())

推荐阅读