在我的代码中,我有一个带有属性的类,有时需要运行异步代码。有时我需要从异步函数访问属性,有时需要从同步函数访问属性-这就是为什么我不希望属性是异步的。此外,我的印象是异步属性通常是代码的味道。如果我错了请纠正我。
从同步属性执行异步方法并阻止进一步执行,直到异步方法完成为止,我遇到了问题。
这是示例代码:
import asyncio
async def main():
print('entering main')
synchronous_property()
print('exiting main')
def synchronous_property():
print('entering synchronous_property')
loop = asyncio.get_event_loop()
try:
# this will raise an exception, so I catch it and ignore
loop.run_until_complete(asynchronous())
except RuntimeError:
pass
print('exiting synchronous_property')
async def asynchronous():
print('entering asynchronous')
print('exiting asynchronous')
asyncio.run(main())
其输出:
entering main
entering synchronous_property
exiting synchronous_property
exiting main
entering asynchronous
exiting asynchronous
首先,RuntimeError
捕获似乎是错误的,但是如果我不这样做,我会得到RuntimeError: This event loop is already running
异常。
第二,asynchronous()
函数在同步完成一次后最后执行。我想通过异步方法对数据集进行一些处理,因此我需要等待它完成。
如果我在调用await asyncio.sleep(0)
之后添加synchronous_property()
,它将在asynchronous()
完成之前调用main()
,但这对我没有帮助。我需要在asynchronous()
完成之前运行synchronous_property()
。
我想念什么?我正在运行python 3.7。
答案 0 :(得分:1)
Asyncio确实坚持不允许嵌套循环by design。但是,您始终可以在其他线程中运行另一个事件循环。这是一种使用线程池来避免每次都必须创建新线程的变体:
import asyncio, concurrent.futures
async def main():
print('entering main')
synchronous_property()
print('exiting main')
pool = concurrent.futures.ThreadPoolExecutor()
def synchronous_property():
print('entering synchronous_property')
result = pool.submit(asyncio.run, asynchronous()).result()
print('exiting synchronous_property', result)
async def asynchronous():
print('entering asynchronous')
await asyncio.sleep(1)
print('exiting asynchronous')
return 42
asyncio.run(main())
此代码在每个sync-> async边界上创建一个新的事件循环,因此,如果您经常这样做,不要指望高性能。可以通过使用asyncio.new_event_loop
每个线程仅创建一个事件循环,并将其缓存在线程局部变量中来进行改进。
答案 1 :(得分:0)
我想进行异步调用以从同步执行并阻止其执行
只需使sync功能异步,然后等待异步功能即可。异步函数就像普通函数一样,您可以将所需的任何代码放入其中。如果仍然有问题,请尝试使用实际代码修改问题。
import asyncio
async def main():
print('entering main')
await synchronous_property()
print('exiting main')
async def synchronous_property():
print('entering synchronous_property')
await asynchronous()
# Do whatever sync stuff you want who cares
print('exiting synchronous_property')
async def asynchronous():
print('entering asynchronous')
print('exiting asynchronous')
asyncio.run(main())
答案 2 :(得分:0)
上述问题似乎有问题。重申问题: 如何在线程(不包含异步进程,因此不考虑同步)和异步过程(在某些事件循环中运行)之间进行通信。一种方法是使用两个同步队列。同步过程将其请求/参数放入QtoAsync中,然后等待QtoSync。异步进程读取QtoAsync,而无需等待,如果找到请求/参数,则执行请求,并将结果放入QtoSync。
import queue
QtoAsync = queue.Queue()
QtoSync = queue.Queue()
...
async def asyncProc():
while True:
try:
data=QtoAsync.get_nowait()
result = await <the async that you wish to execute>
QtoAsync.put(result) #This can block if queue is full. you can use put_nowait and handle the exception.
except queue.Empty:
await asyncio.sleep(0.001) #put a nominal delay forcing this to wait in event loop
....
#start the sync process in a different thread here..
asyncio.run(main()) #main invokes the async tasks including the asyncProc
The sync thread puts it request to async using:
req = <the async that you wish to execute>
QtoAsync.put(req)
result = QtoSync.get()
这应该有效。
问题如下: 1.当使用asyncio.run(或类似程序)启动异步进程时,执行块将停止,直到异步进程完成。在调用asyncio.run之前,必须显式启动一个单独的同步线程。 2.通常,异步进程依赖于该循环中的其他异步进程。因此,不允许直接从另一个线程调用异步进程。交互应该与事件循环进行,使用两个队列是一种方法。
答案 3 :(得分:0)
最简单的方法是使用现有的“滚轮”, 喜欢 asgiref.async_to_sync
from asgiref.sync import async_to_sync
然后:
async_to_sync(main)()
通常:
async_to_sync(<your_async_func>)(<.. arguments for async function ..>)
This is a caller class which turns an awaitable that only works on the thread with the event loop into a synchronous callable that works in a subthread. If the call stack contains an async loop, the code runs there. Otherwise, the code runs in a new loop in a new thread. Either way, this thread then pauses and waits to run any thread_sensitive code called from further down the call stack using SyncToAsync, before finally exiting once the async task returns.