CPU intensive
Sync operation
Run in another thread
Use async lib
IO
CPU
Any "aio-{name}" lib?
async def some_func():
time.sleep(100)import asyncio
def callback():
time.sleep(100)
async def some_func():
await asyncio.get_runing_loop().run_in_executor(None, callback)
import asyncio
import functools
def callback(t=100):
time.sleep(t)
async def some_func():
some_name = functools.partial(callback, t=10)
await asyncio.get_runing_loop().run_in_executor(None, some_name)
import asyncio
def run_in_threadpool(func):
@functools.wraps(func)
def wrap(*args, **kwargs):
def inner():
return func(*args, **kwargs)
return asyncio.get_running_loop().run_in_executor(None, inner)
return wrap
@run_in_threadpool
def callback(t=100):
time.sleep(t)
async def some_func():
await callback(10)
@run_in_threadpool
def some_function(args):
some_values = some_code()
...
...
return a, b, c
async def handler(request):
await something(...)
...
a, b, c = await some_function(...)
some_other_code(b)
return aasync def http_handler(request):
await some_func()
async with threadpool():
time.sleep(100)
a = 'look mom no def but run in another thread'
print(a)
return 'all ok'import asyncio
import threading
import time
from asyncio_extras import threadpool
async def som_handler():
main_thread_id = threading.get_ident()
async with threadpool():
time.sleep(1)
thread_id = threading.get_ident()
assert main_thread_id != thread_id
asyncio.run(som_handler())
class _ThreadSwitcher:
__slots__ = 'executor', 'exited'
def __init__(self, executor: Optional[Executor]) -> None:
self.executor = executor
self.exited = False
def __aenter__(self):
# This is run in the event loop thread
return self
... ...
def __await__(self):
def exec_when_ready():
event.wait()
coro.send(None)
if not self.exited:
raise RuntimeError('attempted to "await" in a worker thread')
if self.exited:
# This is run in the worker thread
yield
else:
# This is run in the event loop thread
previous_frame = inspect.currentframe().f_back
coro = next(obj for obj in gc.get_referrers(previous_frame.f_code)
if inspect.iscoroutine(obj) and obj.cr_frame is previous_frame)
event = Event()
loop = get_event_loop()
future = loop.run_in_executor(self.executor, exec_when_ready)
next(future.__await__()) # Make the future think it's being awaited on
loop.call_soon(event.set)
yield future ...
def __aexit__(self, exc_type, exc_val, exc_tb):
# This is run in the worker thread
self.exited = True
return self
... ...
def __call__(self, func: Callable) -> Callable:
@wraps(func)
def wrapper(*args, **kwargs):
try:
loop = get_event_loop()
except RuntimeError:
# Event loop not available -- we're in a worker thread
return func(*args, **kwargs)
else:
callback = partial(func, *args, **kwargs)
return loop.run_in_executor(self.executor, callback)
assert not inspect.iscoroutinefunction(func), \
'Cannot wrap coroutine functions to be run in an executor'
return wrapperNo difference found
async def some_asyncio_handler():
await save_to_db()
await some_func()
return 'ok'Also any flask app just ignore client disconnects
Run sync like async code, and we can catch CancellError in any place.
try:
val = some_code()
except Exception:
print('oh no')
some_fallback()
try:
val = some_code()
except CancellError:
raise
except Exception:
print('oh no')
some_fallback()
You fully understood _ThreadSwitcher code