问题描述
如果我在异步网络服务器中有一个结构
import contextvars
...
my_context_var = contextvars.Contextvar("var")
@app.route("/foo") # decorator from webserver
async def some_web_endpoint():
local_ctx_var = my_context_var.set(params.get("bar")) # app sets params
await some_function_that_can_raise()
local_ctx_var.reset()
如果我不将 Contextvar
包裹在 finally:
块中并且 some_function_that_can_raise()
引发异常,它会泄漏内存吗?
(没有这种情况,.reset()
永远不会被调用)
try:
await some_function_that_can_raise()
finally:
local_ctx_var.reset()
.. 或者假设该值在请求范围结束时将被销毁是否安全?
The async example in the upstream docs doesn't actually bother .reset()
-ing it at all!
在这种情况下,.reset()
是多余的,因为它发生在上下文被清理之前。
为了添加更多上下文 (ha),我最近正在学习 Contextvars,我认为第二种情况是这样。
local_ctx_var
是唯一引用 Token (from .set()
) 的名称,并且由于名称在请求范围结束时被删除,因此本地值应该成为垃圾收集的候选,防止潜在的泄漏并使 .reset()
对于短期作用域没有必要(万岁)
..但我不是绝对确定,虽然有一些关于这个主题的非常有用的信息,但它稍微混淆了混合物
- What happens if I don't reset Python's ContextVars?(暗示它将按预期进行 GC)
-
Context variables in Python(明确使用
finally:
)
解决方法
是 - 在这种情况下,context_var 的先前值保留在令牌对象中。有这个 rather similar question,其中一个答案运行一个简单的基准测试,以断言多次调用 context_var.set()
并丢弃返回值不会消耗内存,例如与创建新字符串和保持对它的引用。
鉴于基准,我做了一些进一步的实验并得出结论,没有泄漏 - 事实上,在上面的代码中,调用 reset
确实是多余的 - 如果您必须恢复以前的由于某种原因,循环结构中的值。
在最后保存的上下文的顶部设置了新的变量,在此过程中,上下文的当前版本中设置的值被简单地丢弃:对它的唯一引用是剩下的在令牌中,如果有的话。换句话说:以“类似堆栈”的方式保留先前值的是仅调用 contextvars.run
和 contextvars.copy_context
,而不是 Contextvar.set
。