Context variables are convenient when you need to pass a variable along the chain of calls so that they share the same context, in the case when this cannot be done through a global variable in case of concurrency. Context variables can be used as an alternative to global variables both in multi-threaded code and in asynchronous (with coroutines).
Context variables are natively supported in asyncio
and are ready to be used without any extra configuration. Because when a Task
is created it copies the current context and later runs its coroutine in the copied context:
# asyncio/task.py
class Task:
def __init__(self, coro):
...
# Get the current context snapshot.
self._context = contextvars.copy_context()
self._loop.call_soon(self._step, context=self._context)
def _step(self, exc=None):
...
# Every advance of the wrapped coroutine is done in
# the task's context.
self._loop.call_soon(self._step, context=self._context)
...
Below is your example showing the corruption of a global variable compared to context vars:
import asyncio
import contextvars
# declare context var
current_request_id_ctx = contextvars.ContextVar('')
current_request_id_global = ''
async def some_inner_coroutine():
global current_request_id_global
# simulate some async work
await asyncio.sleep(0.1)
# get value
print('Processed inner coroutine of request: {}'.format(current_request_id_ctx.get()))
if current_request_id_global != current_request_id_ctx.get():
print(f"ERROR! global var={current_request_id_global}")
async def some_outer_coroutine(req_id):
global current_request_id_global
# set value
current_request_id_ctx.set(req_id)
current_request_id_global = req_id
await some_inner_coroutine()
# get value
print('Processed outer coroutine of request: {}
'.format(current_request_id_ctx.get()))
async def main():
tasks = []
for req_id in range(1, 10000):
tasks.append(asyncio.create_task(some_outer_coroutine(req_id)))
await asyncio.gather(*tasks)
if __name__ == '__main__':
asyncio.run(main())
Output:
...
Processed inner coroutine of request: 458
ERROR! global var=9999
Processed outer coroutine of request: 458
Processed inner coroutine of request: 459
ERROR! global var=9999
Processed outer coroutine of request: 459
...
An example of converting code that uses threading.local()
can be found in PЕP 567
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…