Python:如何创建一个在每个上下文中都不同的并发安全的可重入上下文管理器
Python: How to create a concurrent-safe reentrant context manager which is different in every context
我想要一个对象 conManager
,它是一个 reentrant context manager 实例,这样每当我进入和退出它的上下文时,它都会打印一个数字,但数字必须比数字大一previus 上下文(从 0 开始)。
示例:
with conManager:
print("Afirst")
with conManager:
print("Asecond")
with conManager:
print("third")
print("Bsecond")
print("Bfirst")
预期输出:
0
Afirst
1
Asecond
2
third
2
Bsecond
1
Bfirst
0
到目前为止我唯一的解决方案是 class 里面有一个堆栈,但这不是并发安全的。有没有并发安全的解决方案?
编辑:正如 Sraw 所指出的,当我指的是并发安全时,我说的是线程安全,因此相应地更改了问题。
我能想到的唯一解决方案是覆盖 conManager
的 _call_ 以便 it returns 一个上下文管理器,但我宁愿有一个更干净的、无调用的用法。
编辑:既然有人表现出兴趣,我想我应该 post 我找到的解决方案
from contextvars import ContextVar
from random import randrange
from weakref import WeakKeyDictionary
context_stack = ContextVar('context_stack')
class ReCm:
def __init__(self):
if not context_stack.get(None):
context_stack.set(WeakKeyDictionary())
context_stack.get()[self] = []
def __enter__(self):
v = randrange(10)
context_stack.get()[self].append(v)
print(f'entered {v}')
return v
def __exit__(self, exc_type, exc_val, exc_tb):
v = context_stack.get()[self].pop()
print(f'exited {v}')
我想要一个对象 conManager
,它是一个 reentrant context manager 实例,这样每当我进入和退出它的上下文时,它都会打印一个数字,但数字必须比数字大一previus 上下文(从 0 开始)。
示例:
with conManager:
print("Afirst")
with conManager:
print("Asecond")
with conManager:
print("third")
print("Bsecond")
print("Bfirst")
预期输出:
0
Afirst
1
Asecond
2
third
2
Bsecond
1
Bfirst
0
到目前为止我唯一的解决方案是 class 里面有一个堆栈,但这不是并发安全的。有没有并发安全的解决方案?
编辑:正如 Sraw 所指出的,当我指的是并发安全时,我说的是线程安全,因此相应地更改了问题。
我能想到的唯一解决方案是覆盖 conManager
的 _call_ 以便 it returns 一个上下文管理器,但我宁愿有一个更干净的、无调用的用法。
编辑:既然有人表现出兴趣,我想我应该 post 我找到的解决方案
from contextvars import ContextVar
from random import randrange
from weakref import WeakKeyDictionary
context_stack = ContextVar('context_stack')
class ReCm:
def __init__(self):
if not context_stack.get(None):
context_stack.set(WeakKeyDictionary())
context_stack.get()[self] = []
def __enter__(self):
v = randrange(10)
context_stack.get()[self].append(v)
print(f'entered {v}')
return v
def __exit__(self, exc_type, exc_val, exc_tb):
v = context_stack.get()[self].pop()
print(f'exited {v}')