You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When using a function decorated with @async_to_sync inside of a function decorated with @sync_to_async a new thread is spawned instead of reusing the same thread and event loop. This issue does not happen when doing async_to_sync(coroutine)().
This is due to the fact that the AsyncToSync sets main_event_loop and main_event_loop_pid on __init__ and not on __call__ and SyncToAsync only sets main_event_loop and main_event_loop_pid on __call__.
The easy way to solve this is moving the initialization of self.main_event_loop and self.main_event_loop_pid from __init__ to __call__ in AsyncToSync (#439 fixes the issue)
Consider this example on why this is relevant:
importasynciofromasgiref.syncimportsync_to_async, async_to_syncimportthreading# this function cannot be changeddeflib_call(callback):
callback.callback()
classCallback:
defcallback():
asyncdefcoro():
print(f"Callback thread: {threading.get_ident()}")
# async code hereasync_to_sync(coro)()
classCallbackDecorated:
@async_to_syncasyncdefcallback():
print(f"Callback decorated thread: {threading.get_ident()}")
# async code here@sync_to_asyncdefblocking_func():
print(f"Blocking func thread: {threading.get_ident()}")
# this uses the outer event looplib_call(Callback())
# this spawns a new thread and new event loop for the callbacklib_call(CallbackDecorated())
asyncdefmain():
try:
asyncwithasyncio.TaskGroup() astg:
task0=tg.create_task(blocking_func())
exceptExceptionasexc:
passprint(f"Main thread: {threading.get_ident()}")
asyncio.run(main())
In this example calling async_to_sync in blocking_func is a bit akward, therefore our only option is to modify the callback method. This however is also akward since we have to create an auxiliary coroutine for it to use the correct thread. The best syntax would be to use the @async_to_sync decorator that is however not thread safe.
The text was updated successfully, but these errors were encountered:
This is interesting because I personally do a lot of async_to_sync(coroutine)() style code so I probably didn't notice this myself!
PR looks good - one linting issue that needs fixing and I can land it.
This is interesting because I personally do a lot of async_to_sync(coroutine)() style code so I probably didn't notice this myself! PR looks good - one linting issue that needs fixing and I can land it.
There is actually one issue I spotted in the PR. In case I run the new decorated function in a thread not started by SyncToAsync, the AsyncToSync object would not have self.main_event_loop initialized, I fixed that issue by moving the check for if there is a running event loop back to __init__ (which made no sense to check in __call__ anyways).
When using a function decorated with
@async_to_sync
inside of a function decorated with@sync_to_async
a new thread is spawned instead of reusing the same thread and event loop. This issue does not happen when doingasync_to_sync(coroutine)()
.This is due to the fact that the AsyncToSync sets
main_event_loop
andmain_event_loop_pid
on__init__
and not on__call__
and SyncToAsync only setsmain_event_loop
andmain_event_loop_pid
on__call__
.The easy way to solve this is moving the initialization of
self.main_event_loop
andself.main_event_loop_pid
from__init__
to__call__
in AsyncToSync (#439 fixes the issue)Consider this example on why this is relevant:
The output is:
In this example calling
async_to_sync
inblocking_func
is a bit akward, therefore our only option is to modify thecallback
method. This however is also akward since we have to create an auxiliary coroutine for it to use the correct thread. The best syntax would be to use the@async_to_sync
decorator that is however not thread safe.The text was updated successfully, but these errors were encountered: