Dans mon code, j'ai une classe avec des propriétés, qui ont parfois besoin d'exécuter du code asynchrone. Parfois, j'ai besoin d'accéder à la propriété à partir de la fonction asynchrone, parfois à partir de la synchronisation - c'est pourquoi je ne veux pas que mes propriétés soient asynchrones. D'ailleurs, j'ai l'impression que les propriétés asynchrones en général sont une odeur de code. Corrige moi si je me trompe.
J'ai un problème avec l'exécution de la méthode asynchrone à partir de la propriété synchrone et le blocage de l'exécution supplémentaire jusqu'à la fin de la méthode asynchrone.
Voici un exemple de code:
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())
Sa sortie:
entering main
entering synchronous_property
exiting synchronous_property
exiting main
entering asynchronous
exiting asynchronous
Tout d'abord, la capture de RuntimeError
semble incorrecte, mais si je ne le fais pas, j'obtiendrai l'exception RuntimeError: This event loop is already running
.
Deuxièmement, la fonction asynchronous()
est exécutée en dernier, après la fin synchrone. Je veux effectuer un traitement sur l'ensemble de données par une méthode asynchrone, je dois donc attendre qu'il se termine. Si j'ajoute await asyncio.sleep(0)
après avoir appelé synchronous_property()
, il appellera asynchronous()
avant que main()
termine, mais cela ne m'aide pas. J'ai besoin d'exécuter asynchronous()
avant que synchronous_property()
termine.
Qu'est-ce que je rate? J'exécute python 3.7.
Il semble y avoir un problème avec la question comme indiqué. Reformulation de la question: comment communiquer entre un thread (ne contenant aucun processus asynchrone et donc considéré comme sync) et un processus asynchrone (fonctionnant dans une boucle d'événement). Une approche consiste à utiliser deux files d'attente de synchronisation. Le processus de synchronisation place sa demande/ses paramètres dans le QtoAsync et attend sur le QtoSync. Le processus asynchrone lit QtoAsync SANS attente, et s'il trouve une requête/des paramètres, exécute la requête et place le résultat dans 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()
Cela devrait fonctionner.
Problème avec la question comme indiqué: 1. Lorsque les processus asynchrones sont démarrés avec des blocs d'exécution asyncio.run (ou similaire) jusqu'à la fin des processus asynchrones. Un thread de synchronisation distinct doit être démarré explicitement avant d'appeler asyncio.run 2. En général, les processus asyncio dépendent d'autres processus asyncio dans cette boucle. Ainsi, l'appel d'un processus asynchrone à partir d'un autre thread n'est pas autorisé directement. L'interaction doit être avec la boucle d'événements, et l'utilisation de deux files d'attente est une approche.
La manière la plus simple est d'utiliser une "roue" existante, comme asgiref.async_to_sync
from asgiref.sync import async_to_sync
puis:
async_to_sync(main)()
en général:
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.