3

如果我在异步网络服务器中有一个结构,例如

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()

.. 或者假设请求范围结束时该值将被销毁是否安全?
上游文档中的异步示例实际上根本不打扰.reset()
在这种情况下,.reset()这是多余的,因为它发生在上下文被清理之前。


为了添加更多上下文(ha),我最近正在学习 ContextVars,我假设是第二种情况。

local_ctx_var是唯一引用 Token ( from.set() ) 的名称,并且当请求范围结束时名称被删除,本地值应该成为垃圾收集的候选者,防止潜在的泄漏并使.reset()短期范围变得不必要(万岁)

..但我不确定,虽然有一些关于这个主题的非常有用的信息,但它稍微混淆了混合物

4

1 回答 1

3

是 - 在这种情况下,context_var 的先前值保存在令牌对象中。有一个相当相似的问题,其中一个答案运行一个简单的基准来断言context_var.set()多次调用并丢弃返回值不会消耗内存,与创建一个新字符串并保持对它的引用相比。

鉴于基准,我做了一些进一步的实验并得出结论没有泄漏 - 事实上,在像上面这样的代码中,调用reset确实是多余的 - 如果由于某种原因必须在循环构造中恢复以前的值,这很有用.

在最后保存的上下文之上设置了新的 var,在当前版本的上下文中设置的值在此过程中被简单地丢弃:对它的唯一引用是标记中剩下的那个,如果有的话。换句话说:以“类似堆栈”的方式保留先前值的是对contextvars.runcontextvars.copy_context仅调用,而不是调用Contextvar.set

于 2021-04-16T15:30:15.560 回答